NOMOR 3: Analisis Klasifikasi Komprehensif

Narasi

PDM Paylater adalah perusahaan fiktif penyedia layanan Buy Now Pay Later (BNPL) yang utamanya mendapatkan pendapatan dengan memberikan pinjaman uang. Salah satu risiko utama dalam praktik ini adalah kemungkinan peminjam gagal membayar pinjamannya, dengan menghentikan pembayaran sesuai kesepakatan. Hal ini akan menyebabkan kerugian keuangan bagi perusahaan.

Untuk mengurangi kerugian tersebut, sangat penting bagi PDM Paylater untuk membuat keputusan yang terinformasi (berbasiskan data) tentang kepada siapa memberikan pinjaman. Pada dasarnya, mereka perlu menilai risiko yang terkait dengan setiap peminjam dan membuat keputusan pemberian pinjaman berdasarkan hal itu.

Tugas Anda adalah membantu PDM Paylater dalam mengatasi tantangan ini. Anda akan melakukannya dengan mengevaluasi kemampuan setiap calon peminjam untuk melakukan pembayaran yang akan memungkinkan pengambilan keputusan pemberian pinjaman yang terinformasi dengan baik. Model ini akan membantu menentukan calon peminjam mana yang layak mendapatkan pinjaman.

Dataset

Dataset yang digunakan dapat diakses melalui link berikut: Dataset Nomor 3

  • variabel_description.csv: metadata
  • train.csv: Dataset utama yang digunakan dalam pembentukan model klasifikasi, berisi informasi setiap aktivitas pengajuan pinjaman (aplikasi) oleh seorang calon peminjaman. Label target yang digunakan adalah FLAG, menunjukan apakah seorang calon peminjam akan diberikan pinjaman (1) atau tidak (0). Hal ini didasarkan pada prediksi apakah seorang calon peminjam akan sanggup dalam memenuhi kewajibannya melunasi pinjaman dengan tepat waktu atau tidak.
  • test.csv: Digunakan dalam uji performa (evaluasi) model terbaik yang dibentuk menggunakan dataset train.csv. Berisi informasi setiap aktivitas pengajuan pinjaman oleh seorang calon peminjam. Label target yang digunakan adalah FLAG.
  • previous_application.csv: Berisi informasi tentang data pengajuan pinjaman (aplikasi) sebelumnya. Terhubung dengan data train.csv dan test.csv berdasarkan fitur U_ID.
  • payment_history.csv: Data pembayaran yang lalu untuk setiap pinjaman sebelumnya. Terhubung dengan data train.csv dan test.csv berdasarkan fitur U_ID, serta terhubung dengan data previous_application.csv berdasarkan fitur SK_ID_PREV.

Tugas

Peserta dibebaskan menggunakan semua dataset, dengan 2 dataset utama yang wajib digunakan adalah train.csv (untuk pemodelan) dan test.csv (untuk evaluasi).

  1. Jelaskan langkah-langkah pra-pemrosesan data (Preprocessing) yang Anda lakukan secara detail dan sejelas mungkin.
  • Pada Notebook ini tahapan preprocessing terbagi menjadi 3 bagian
    • Bagian 1: Dilakukan data cleaning dengan tujuan memperbaiki atau menghapus kesalahan, ketidakkonsistenan, dan ketidakakuratan dalam kumpulan data. Identifikasi hal ini cukup dilakukan pada dataset train.csv dan test.csv (karena yang wajib digunakan dalam analisis) serta dilakukan secara general berdasarkan eksplorasi awal.
    • Bagian 2: Pada bagian ini dilakukan pra-pemrosesan data dan feature engineering melanjutkan proses data cleaning sebelumnya untuk digunakan dalam proses pemodelan berdasarkan insight yang diperoleh pada bagian EDA.
    • Bagian 3: Beberapa tahapan preprocessing data menggunakan operator pipeline pada scikit-learn agar lebih mudah digunakan dan menghindari kemungkinan terjadinya kebocoran (leakage) data tes saat pemodelan.
  • Peserta diwajibkan membuat setidaknya 3 fitur baru, dengan mengekstrak nilai fitur dari dataset train.csv, test.csv, previous_application.csv dan payment_history.csv. Transformasi data dengan mengubah satuan (seperti hari menjadi tahun) tidak dihitung sebagai pembentukan fitur baru. Dataset utama dalam analisis ini adalah train.csv dan test.csv sehingga hasil fitur baru digabungkan dengan setiap dataset ini (tidak boleh salah satu).
  • Peserta diwajibkan menggunakan setidaknya 3 metode preprocessing data berbeda pada tahapan preprocessing bagian 3 (pipeline). Jika dilakukan imputasi missing value menggunakan modus untuk data kategorik dan mean untuk data numerik akan dihitung sebagai 1 metode preprocessing yang sama.
  1. Lakukanlah tahapan Exploratory Data Analysis (EDA) untuk mendapatkan insight yang bermanfaat dari data. Pada tahapan ini anda harus membuat setidaknya 5 pertanyaan yang akan dijawab berdasarkan hasil eksplorasi dan visualisasi serta wajib menjawab 3 pertanyaan berikut:
  • Bagaimana sebaran proporsi pada label target (FLAG)?
  • Bagaimana pola jumlah pinjaman yang disetujui (APPROVED_CREDIT) dan karakteristiknya berdasarkan jenis kontrak (CONTRACT_TYPE)?
  • Terdapat korelasi yang kuat antara 3 fitur, yaitu Product Price, Loan Annuity dan Approved Credit (tunjukan dengan heatmap correlation). Dalam hal ini, pemodelan klasifikasi nantinya hanya akan menggunakan fitur Product Price. Kenapa demikian? (Hint: pertimbangkan ketersediaan data pada saat calon peminjam baru mengajukan pinjaman)
  • Tambahkan 5 pertanyaan lainnya dan jawab berdasarkan hasil eksplorasi dan visualisasi data.
  1. Pilihlah metrik evaluasi yang Anda gunakan untuk menilai performa model (tahapan pemodelan serta pemilihan model terbaik) dan berikan penjelasan menyeluruh tentang alasan Anda memilih metrik tersebut. Pada bagian evaluasi akhir (uji performa menggunakan data test.csv) anda wajib menginterpretasikan setidaknya menggunakan 3 metrik evaluasi yang berbeda.
  2. Pilihlah model terbaik dengan membandingkan beberapa model klasifikasi (minimal 7 model yang berbeda) dan berikan penjelasan mengapa model tersebut dipilih sebagai yang terbaik.
  3. Lakukanlah uji performa pada data test.csv dengan menggunakan setidaknya 3 metrik evaluasi yang berbeda dan berikan interpretasi.
  4. Berikan penjelasan tentang fitur-fitur yang paling penting pada model terbaik yang dipilih dan jelaskan bagaimana dan mengapa fitur-fitur tersebut sangat penting.
  5. Berikan juga rekomendasi & kesimpulan menyeluruh terhadap bisnis PDM Paylater berdasarkan hasil analisis yang telah dilakukan.

Hint

  • Contoh pembuatan fitur baru: Dibentuk fitur PRE_LATE
    • Nilainya berupa 1: Pernah telat bayar, 0: Tidak pernah telat bayar
    • Diperoleh dari dataset payment_history dengan membandingkan fitur nilai pada Ints days (waktu jatuh jempo) dan Pays Days (waktu dibayar sebenarnya) yang kemudian di aggregate jumlahnya berdasarkan fitur U_ID, sehingga diperoleh insight apakah seorang calon peminjam pernah telat dalam membayar pada peminjaman sebelumnya (jumlah telat > 1 diubah nilainya menjadi 1)
    • Hasilnya digabungkan ke dataset train dan test berdasarkan fitur U_ID dengan metode penggabungan yang mempertahankan seluruh baris observasi pada kedua dataset ini (train dan test).
    • Jika terdapat nilai NA pada fitur PRE_LATE (yang telah digabungkan ke data train dan test), maka diimputasi dengan nilai 0. Artinya belum ada riwayat peminjaman sebelumnya oleh calon peminjam tersebut.
  • Referensi operator pipeline pada scikit learn
  • Referensi Feature Importance
  • Referensi SHAP values
  • Referensi SHAP values individual/SHAP Force Plot

Perhatikan!!!

  • Tahapan pemodelan dan EDA hanya menggunakan data train.csv. Tahapan evaluasi akhir menggunakan data test.csv. Dataset lainnya digunakan dalam feature engineering dengan membuat fitur baru pada dataset train.csv dan test.csv (wajib keduanya).

  • Hati-hati pada saat penggabungan fitur baru dengan dataset train.csv dan test.csv

  • Peserta diizinkan melewatkan operator pipeline pada preprocessing bagian 3, tetapi akan mendapatkan pengurangan nilai.

  • Gunakan alur seperti yang ditunjukan pada Outline dibawah untuk mempermudah analisis, detailnya dapat disesuaikan dengan preferensi masing-masing.

  • Peserta dilarang melakukan optimalisasi model menggunakan metode hyperparameter tunning dan sejenisnya. Oleh karena itu, peserta dilarang mengatur parameter setiap model selain random_state dan verbose. Contoh yang benar:

    • RidgeClassifier(random_state=0)
    • RandomForestClassifier(random_state=0)
    • CatBoostClassifier(random_state=0, verbose=0)

      Dilarang seperti berikut:
    • XGBClassifier(random_state=0, n_estimators = 500, learning_rate= 0.01, max_depth = 5)
  • Hasil splitting pada data train.csv akan disebut sebagai data train dan data validasi. Peserta bebas dalam pengaturan proporsi dan penggunaan random_state pada splitting data. Data latih (train) sekurang-kurangnya 60% dari train.csv

  • Peserta diizinkan untuk dengan/tidak menggunakan berbagai metode penanganan data tidak seimbang dengan memperhatikan data leakage.

  • Peserta dilarang menambah dan/atau menghapus observasi (baris) pada dataset test.csv

  • Untuk mempermudah interpretasi pada tahapan Post Analysis, peserta diharapkan tidak menggunakan metode encoding dengan One Hot Encoding atau dummy. Disarankan menggunakan metode Label Encoder atau Ordinal Encoder.

  • Bagian Post Analysis cukup dilakukan dengan menjalankan syntax yang disediakan dan memberikan interpretasi output (Feature Importance)

Bonus Point

  • Peserta membuat metrik evaluasi sendiri secara logis menggunakan fungsi make_scorer pada scikit learn
  • Membuat fungsi perulangan yang menggabungkan operator pipeline dan kandidat model serta menghasilkan output performa masing-masing model.
  • Terdapat fitur baru yang dibentuk yang masuk kedalam 5 fitur terbaik pada plot Feature Importance
  • Melanjutkan tahapan Post Analysis menggunakan konsep SHAP values untuk mengetahui bagaimana pola pengaruh fitur terhadap label target (FLAG) dan SHAP Force Plot / SHAP values individual** dengan 2 sampel (individu) data pengajuan pinjaman.

Outline

0 - Intro

Library

In [1]:
# Impor library yang akan digunakan
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly
import plotly.graph_objects as go
import plotly.offline as pyo
from plotly.offline import iplot
import warnings
warnings.filterwarnings("ignore")

from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, classification_report, roc_auc_score, roc_curve
from sklearn.preprocessing import OrdinalEncoder

from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier

# Mengatur tampilan untuk seaborn
sns.set(context= "notebook", color_codes=True)
plt.style.use('bmh')

pyo.init_notebook_mode()

%matplotlib inline

Metadata

In [2]:
## Jika menggunakan google colab dan dataset pada google drive, jalankan 2 baris syntax berikut
# from google.colab import drive
# drive.mount('/content/drive')

# Impor data variabel description
df_var = pd.read_csv('/Users/revaldyhazzadaniswara/Downloads/variable_description.csv')
pd.set_option('display.max_colwidth', None)
display(df_var)
Table Columns Description
0 loanapptrain.csv / loanapptest.csv U_ID Loan ID
1 loanapptrain.csv / loanapptest.csv FLAG Target variable (1 - client with late payment more than X days, 0 - all other cases)
2 loanapptrain.csv / loanapptest.csv CONTRACT_TYPE Identification if loan is cash or revolving
3 loanapptrain.csv / loanapptest.csv GENDER Gender of the client
4 loanapptrain.csv / loanapptest.csv NUM_OF_CHILDREN Number of children the client has
5 loanapptrain.csv / loanapptest.csv INCOME Monthly income of the client
6 loanapptrain.csv / loanapptest.csv APPROVED_CREDIT Approved credit amount of the loan
7 loanapptrain.csv / loanapptest.csv LOAN_ANNUITY Loan annuity (amount that must be paid monthly)
8 loanapptrain.csv / loanapptest.csv PRODUCT_PRICE For consumer loans it is the price of the goods for which the loan is given
9 loanapptrain.csv / loanapptest.csv INCOME_CATEGORY Clients income type (businessman, working, maternity leave,…)
10 loanapptrain.csv / loanapptest.csv EDUCATION The client highest education
11 loanapptrain.csv / loanapptest.csv FAMILY_STATUS Family status of the client
12 loanapptrain.csv / loanapptest.csv HOUSING_CATEGORY What is the housing situation of the client (renting, living with parents, ...)
13 loanapptrain.csv / loanapptest.csv DAYS_AGE Client's age in days at the time of application
14 loanapptrain.csv / loanapptest.csv DAYS_WORK How many days before the application the person started current job
15 loanapptrain.csv / loanapptest.csv DAYS_REGISTRATION How many days before the application did client change his registration
16 loanapptrain.csv / loanapptest.csv DAYS_SINCE_ID_CHANGE How many days before the application did client change the identity document with which he applied for the loan
17 loanapptrain.csv / loanapptest.csv APPLY_DAYS On which day of the week did the client apply for the loan
18 loanapptrain.csv / loanapptest.csv APPLY_HOUR Approximately at what hour did the client apply for the loan
19 loanapptrain.csv / loanapptest.csv ORGANIZATION_CATEGORY Type of organization where client works
20 loanapptrain.csv / loanapptest.csv EXTERNAL_SCORE_1 Normalized score from external data source
21 loanapptrain.csv / loanapptest.csv EXTERNAL_SCORE_2 Normalized score from external data source
22 loanapptrain.csv / loanapptest.csv EXTERNAL_SCORE_3 Normalized score from external data source
23 prevloanapp.csv SK_ID_PREV ID of previous loan (One loan can have 0,1,2 or more previous loan application)
24 prevloanapp.csv U_ID Loan ID
25 prevloanapp.csv CONTRACT_TYPE Contract product type (Cash loan, consumer loan [POS] ,...) of the previous application
26 prevloanapp.csv LOAN_ANNUITY Loan annuity (amount that must be paid monthly) of previous application
27 prevloanapp.csv APPLICATION For how much credit did client ask on the previous application
28 prevloanapp.csv APPROVED_CREDIT Final approved credit amount on the previous application. This differs from APPLICATION in a way that the APPLICATION is the amount for which the client initially applied for, but during our approval process he could have received different amount - AMT_CREDIT
29 prevloanapp.csv AMT_DOWN_PAYMENT Down payment on the previous application
30 prevloanapp.csv PRODUCT_PRICE For consumer loans it is the price of the goods for which the loan is given
31 prevloanapp.csv APPLY_DAYS On which day of the week did the client apply for the previous loan
32 prevloanapp.csv APPLY_HOUR Approximately at what hour did the client apply for the previous loan
33 prevloanapp.csv CONTRACT_STATUS Contract status (approved, cancelled, ...) of previous application
34 prevloanapp.csv DAYS_DECISION Relative to current application when was the decision about previous application made
35 prevloanapp.csv TERM_PAYMENT Term of previous credit at application of the previous application
36 prevloanapp.csv YIELD_GROUP Grouped interest rate into small medium and high of the previous application
37 prevloanapp.csv FIRST_DRAW Relative to application date of current application when was the first disbursement of the previous application (in days)
38 prevloanapp.csv FIRST_DUE Relative to application date of current application when was the first due supposed to be of the previous application (in days)
39 prevloanapp.csv TERMINATION Relative to application date of current application when was the expected termination of the previous application
40 prevloanapp.csv NFLAG_INSURED_ON_APPROVAL Did the client requested insurance during the previous application
41 paymenthistory.csv SK_ID_PREV ID of previous loan (One loan can have 0,1,2 or more previous loan application)
42 paymenthistory.csv U_ID Loan ID
43 paymenthistory.csv INST_NUMBER On which installment we observe payment
44 paymenthistory.csv INST_DAYS When the installment of previous credit was supposed to be paid (relative to application date of current loan)
45 paymenthistory.csv PAY_DAYS When was the installments of previous credit paid actually (relative to application date of current loan)
46 paymenthistory.csv AMT_INST What was the prescribed installment amount of previous credit on this installment
47 paymenthistory.csv AMT_PAY What the client actually paid on previous credit on this installment
In [3]:
# Mengetahui jumlah dan tipe data variable description
df_var.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 48 entries, 0 to 47
Data columns (total 3 columns):
 #   Column       Non-Null Count  Dtype 
---  ------       --------------  ----- 
 0   Table        48 non-null     object
 1   Columns      48 non-null     object
 2   Description  48 non-null     object
dtypes: object(3)
memory usage: 1.2+ KB

Dataset

In [4]:
# Impor data yang akan digunakan
df_tr = pd.read_csv('/Users/revaldyhazzadaniswara/Downloads/train.csv')
df_te = pd.read_csv('/Users/revaldyhazzadaniswara/Downloads/test.csv')
df_pre = pd.read_csv('/Users/revaldyhazzadaniswara/Downloads/previous_applications.csv')
df_his = pd.read_csv('/Users/revaldyhazzadaniswara/Downloads/payment_history.csv')
In [5]:
# Menampilkan data train dan data test
display(df_tr.head(), df_te.head())
U_ID FLAG CONTRACT_TYPE GENDER NUM_OF_CHILDREN INCOME APPROVED_CREDIT LOAN_ANNUITY PRODUCT_PRICE INCOME_CATEGORY ... DAYS_AGE DAYS_WORK DAYS_REGISTRATION DAYS_SINCE_ID_CHANGE APPLY_DAYS APPLY_HOUR ORGANIZATION_CATEGORY EXTERNAL_SCORE_1 EXTERNAL_SCORE_2 EXTERNAL_SCORE_3
0 333538 0 Revolving loans F 1 67500.0 202500.0 10125.0 202500.0 Working ... -11539 -921 -119.0 -2757 TUESDAY 18 Business Entity Type 3 0.572805 0.608276 NaN
1 406644 0 Cash loans F 1 202500.0 976711.5 49869.0 873000.0 Commercial associate ... -15743 -4482 -1797.0 -2455 TUESDAY 14 Other 0.655600 0.684298 NaN
2 259130 0 Cash loans F 0 180000.0 407520.0 25060.5 360000.0 Pensioner ... -20775 365243 -8737.0 -4312 THURSDAY 14 NA1 NaN 0.580687 0.749022
3 411997 0 Cash loans M 0 225000.0 808650.0 26086.5 675000.0 State servant ... -20659 -10455 -4998.0 -4010 WEDNESDAY 10 Culture NaN 0.623740 0.710674
4 241559 0 Revolving loans M 0 135000.0 180000.0 9000.0 180000.0 Commercial associate ... -9013 -1190 -3524.0 -1644 SUNDAY 11 Construction 0.175511 0.492994 0.085595

5 rows × 23 columns

U_ID FLAG CONTRACT_TYPE GENDER NUM_OF_CHILDREN INCOME APPROVED_CREDIT LOAN_ANNUITY PRODUCT_PRICE INCOME_CATEGORY ... DAYS_AGE DAYS_WORK DAYS_REGISTRATION DAYS_SINCE_ID_CHANGE APPLY_DAYS APPLY_HOUR ORGANIZATION_CATEGORY EXTERNAL_SCORE_1 EXTERNAL_SCORE_2 EXTERNAL_SCORE_3
0 219092 0 Cash loans M 3 135000.0 871029.0 44604.0 765000.0 Working ... -17598 -2650 -1411.0 -1131 SATURDAY 7 Business Entity Type 3 NaN 0.145475 0.651260
1 141577 0 Cash loans F 0 144000.0 485640.0 34537.5 450000.0 Working ... -14097 -7408 -7908.0 -4872 MONDAY 14 Kindergarten NaN 0.682675 NaN
2 180205 0 Cash loans F 1 90000.0 247500.0 8887.5 247500.0 Working ... -18384 -2826 -8226.0 -1930 SATURDAY 12 Self-employed 0.814700 0.686312 0.758393
3 357381 0 Cash loans M 2 112500.0 506889.0 24781.5 418500.0 Working ... -12170 -926 -916.0 -4048 THURSDAY 13 Other 0.399219 0.266520 0.058826
4 271229 0 Cash loans M 0 216000.0 450000.0 21888.0 450000.0 Working ... -10790 -577 -4640.0 -2035 MONDAY 14 Business Entity Type 3 0.368452 0.610483 0.392774

5 rows × 23 columns

In [6]:
# Menampilkan data previous application dan payment history
display(df_pre.head(), df_his.head())
SK_ID_PREV U_ID CONTRACT_TYPE LOAN_ANNUITY APPLICATION APPROVED_CREDIT AMT_DOWN_PAYMENT PRODUCT_PRICE APPLY_DAYS APPLY_HOUR CONTRACT_STATUS DAYS_DECISION TERM_PAYMENT YIELD_GROUP FIRST_DRAW FIRST_DUE TERMINATION NFLAG_INSURED_ON_APPROVAL
0 2030495 271877 Consumer loans 1730.430 17145.0 17145.0 0.0 17145.0 SATURDAY 15 Approved -73 12.0 middle 365243.0 -42.0 -37.0 0.0
1 2819243 176158 Cash loans 47041.335 450000.0 470790.0 NaN 450000.0 MONDAY 7 Approved -512 12.0 middle 365243.0 -482.0 -177.0 1.0
2 1383531 199383 Cash loans 23703.930 315000.0 340573.5 NaN 315000.0 SATURDAY 8 Approved -684 18.0 low_normal 365243.0 -654.0 -137.0 1.0
3 2315218 175704 Cash loans NaN 0.0 0.0 NaN NaN TUESDAY 11 Canceled -14 NaN NA1 NaN NaN NaN NaN
4 1715995 447712 Cash loans 11368.620 270000.0 335754.0 NaN 270000.0 FRIDAY 7 Approved -735 54.0 low_normal 365243.0 -705.0 -334.0 1.0
SK_ID_PREV U_ID INST_NUMBER INST_DAYS PAY_DAYS AMT_INST AMT_PAY
0 1137312 164489 12 -1384.0 -1417.0 5970.375 5970.375
1 2723183 112102 14 -197.0 -197.0 70.740 70.740
2 2558880 154793 8 -1262.0 -1269.0 15031.080 15031.080
3 1410565 197687 1 -1037.0 -1048.0 12514.050 12510.450
4 2391610 183431 20 -1680.0 -1693.0 7875.000 7875.000
In [7]:
# Deskriptif Statistik untuk tiap data yang digunakan
display(df_tr.describe(), df_te.describe(), df_pre.describe(), df_his.describe())
U_ID FLAG NUM_OF_CHILDREN INCOME APPROVED_CREDIT LOAN_ANNUITY PRODUCT_PRICE DAYS_AGE DAYS_WORK DAYS_REGISTRATION DAYS_SINCE_ID_CHANGE APPLY_HOUR EXTERNAL_SCORE_1 EXTERNAL_SCORE_2 EXTERNAL_SCORE_3
count 61503.000000 61503.000000 61503.000000 6.150300e+04 6.150300e+04 61502.000000 6.144100e+04 61503.000000 61503.000000 61503.000000 61503.000000 61503.000000 26658.000000 6.136900e+04 49264.000000
mean 278151.136416 0.080793 0.411850 1.690789e+05 5.976899e+05 27083.323315 5.369033e+05 -16057.481033 64882.262768 -4976.696324 -2996.374681 12.055136 0.504081 5.141378e-01 0.510787
std 102918.206671 0.272519 0.716686 1.339697e+05 4.016493e+05 14470.508291 3.685742e+05 4363.536919 142154.440320 3520.926325 1512.409598 3.273323 0.210390 1.912087e-01 0.194541
min 100009.000000 0.000000 0.000000 2.610000e+04 4.500000e+04 2164.500000 4.500000e+04 -25229.000000 -17912.000000 -22928.000000 -6274.000000 0.000000 0.018334 8.173617e-08 0.000527
25% 188767.000000 0.000000 0.000000 1.125000e+05 2.700000e+05 16506.000000 2.385000e+05 -19716.500000 -2761.000000 -7479.000000 -4304.000000 10.000000 0.336294 3.926921e-01 0.370650
50% 278357.000000 0.000000 0.000000 1.485000e+05 5.124465e+05 24853.500000 4.500000e+05 -15753.000000 -1208.000000 -4498.000000 -3263.000000 12.000000 0.508522 5.658808e-01 0.535276
75% 367424.500000 0.000000 1.000000 2.025000e+05 8.086500e+05 34596.000000 6.795000e+05 -12450.000000 -276.000000 -1996.000000 -1713.000000 14.000000 0.677160 6.637879e-01 0.667458
max 456255.000000 1.000000 9.000000 1.800009e+07 4.050000e+06 258025.500000 4.050000e+06 -7673.000000 365243.000000 0.000000 0.000000 23.000000 0.951624 8.549997e-01 0.896010
U_ID FLAG NUM_OF_CHILDREN INCOME APPROVED_CREDIT LOAN_ANNUITY PRODUCT_PRICE DAYS_AGE DAYS_WORK DAYS_REGISTRATION DAYS_SINCE_ID_CHANGE APPLY_HOUR EXTERNAL_SCORE_1 EXTERNAL_SCORE_2 EXTERNAL_SCORE_3
count 14761.000000 14761.000000 14761.000000 1.476100e+04 1.476100e+04 14761.000000 1.474100e+04 14761.000000 14761.000000 14761.000000 14761.000000 14761.000000 6449.000000 14742.000000 11842.000000
mean 278759.502879 0.081499 0.411828 1.674965e+05 5.961318e+05 27127.497764 5.361929e+05 -16024.982115 65527.059820 -4989.848249 -3009.099790 12.067475 0.507092 0.515809 0.511901
std 102112.465678 0.273608 0.709092 9.538766e+04 3.996861e+05 14473.650999 3.665194e+05 4369.316681 142694.978431 3523.896416 1507.562327 3.242051 0.211769 0.189662 0.195482
min 100054.000000 0.000000 0.000000 2.655000e+04 4.500000e+04 2187.000000 4.500000e+04 -25165.000000 -16375.000000 -21865.000000 -6337.000000 0.000000 0.017095 0.000013 0.000527
25% 190910.000000 0.000000 0.000000 1.125000e+05 2.700000e+05 16654.500000 2.385000e+05 -19705.000000 -2784.000000 -7576.000000 -4310.000000 10.000000 0.339551 0.395380 0.370650
50% 278940.000000 0.000000 0.000000 1.440000e+05 5.094000e+05 24822.000000 4.500000e+05 -15682.000000 -1221.000000 -4527.000000 -3290.000000 12.000000 0.511271 0.566284 0.537070
75% 366655.000000 0.000000 1.000000 2.025000e+05 8.086500e+05 34573.500000 6.795000e+05 -12381.000000 -275.000000 -1975.000000 -1728.000000 14.000000 0.677166 0.663389 0.670652
max 456249.000000 1.000000 5.000000 2.930026e+06 4.050000e+06 177826.500000 4.050000e+06 -7689.000000 365243.000000 0.000000 0.000000 23.000000 0.944420 0.855000 0.882530
SK_ID_PREV U_ID LOAN_ANNUITY APPLICATION APPROVED_CREDIT AMT_DOWN_PAYMENT PRODUCT_PRICE APPLY_HOUR DAYS_DECISION TERM_PAYMENT FIRST_DRAW FIRST_DUE TERMINATION NFLAG_INSURED_ON_APPROVAL
count 3.507120e+05 350712.000000 274103.000000 3.507120e+05 3.507120e+05 1.642050e+05 2.710720e+05 350712.000000 350712.000000 274103.000000 211407.000000 211407.000000 211407.000000 211407.000000
mean 1.923528e+06 278626.100136 15793.209924 1.747205e+05 1.958564e+05 6.667307e+03 2.260759e+05 12.474438 -877.824856 16.074268 342238.632122 13228.355887 82378.742109 0.331980
std 5.322171e+05 102733.229023 14601.033712 2.924484e+05 3.185176e+05 2.024318e+04 3.147324e+05 3.326745 781.578114 14.595200 88866.480608 71030.663471 153553.036032 0.470925
min 1.000020e+06 100009.000000 0.000000 0.000000e+00 0.000000e+00 -4.500000e-01 0.000000e+00 0.000000 -2922.000000 0.000000 -2922.000000 -2892.000000 -2845.000000 0.000000
25% 1.463183e+06 189728.000000 6258.915000 1.957500e+04 2.473650e+04 0.000000e+00 4.966091e+04 10.000000 -1307.000000 6.000000 365243.000000 -1625.000000 -1267.000000 0.000000
50% 1.923226e+06 279204.000000 11201.085000 7.029000e+04 8.009285e+04 1.768500e+03 1.101150e+05 12.000000 -581.000000 12.000000 365243.000000 -825.000000 -495.000000 0.000000
75% 2.384276e+06 367667.250000 20356.065000 1.800000e+05 2.156400e+05 7.677000e+03 2.295000e+05 15.000000 -271.000000 24.000000 365243.000000 -408.000000 -43.000000 1.000000
max 2.845381e+06 456255.000000 418058.145000 4.050000e+06 4.104351e+06 2.150100e+06 4.050000e+06 23.000000 -2.000000 84.000000 365243.000000 365243.000000 365243.000000 1.000000
SK_ID_PREV U_ID INST_NUMBER INST_DAYS PAY_DAYS AMT_INST AMT_PAY
count 2.872306e+06 2.872306e+06 2.872306e+06 2.872306e+06 2.871633e+06 2.872306e+06 2.871633e+06
mean 1.902798e+06 2.785208e+05 1.865887e+01 -1.039830e+03 -1.048684e+03 1.692881e+04 1.708792e+04
std 5.358735e+05 1.026814e+05 2.635638e+01 7.995411e+02 7.991129e+02 5.010468e+04 5.422172e+04
min 1.000020e+06 1.000090e+05 1.000000e+00 -2.922000e+03 -3.129000e+03 0.000000e+00 0.000000e+00
25% 1.435627e+06 1.893100e+05 4.000000e+00 -1.651000e+03 -1.659000e+03 4.199850e+03 3.389490e+03
50% 1.894453e+06 2.786890e+05 8.000000e+00 -8.170000e+02 -8.260000e+02 8.787330e+03 8.095050e+03
75% 2.368624e+06 3.675770e+05 1.900000e+01 -3.580000e+02 -3.670000e+02 1.661709e+04 1.597320e+04
max 2.843498e+06 4.562550e+05 2.250000e+02 -2.000000e+00 -2.000000e+00 3.371884e+06 3.371884e+06
In [8]:
# Menampilkan informasi dasar data yang akan digunakan
display(df_tr.info(), df_te.info(), df_his.info(), df_pre.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 61503 entries, 0 to 61502
Data columns (total 23 columns):
 #   Column                 Non-Null Count  Dtype  
---  ------                 --------------  -----  
 0   U_ID                   61503 non-null  int64  
 1   FLAG                   61503 non-null  int64  
 2   CONTRACT_TYPE          61503 non-null  object 
 3   GENDER                 61503 non-null  object 
 4   NUM_OF_CHILDREN        61503 non-null  int64  
 5   INCOME                 61503 non-null  float64
 6   APPROVED_CREDIT        61503 non-null  float64
 7   LOAN_ANNUITY           61502 non-null  float64
 8   PRODUCT_PRICE          61441 non-null  float64
 9   INCOME_CATEGORY        61503 non-null  object 
 10  EDUCATION              61503 non-null  object 
 11  FAMILY_STATUS          61503 non-null  object 
 12  HOUSING_CATEGORY       61503 non-null  object 
 13  DAYS_AGE               61503 non-null  int64  
 14  DAYS_WORK              61503 non-null  int64  
 15  DAYS_REGISTRATION      61503 non-null  float64
 16  DAYS_SINCE_ID_CHANGE   61503 non-null  int64  
 17  APPLY_DAYS             61503 non-null  object 
 18  APPLY_HOUR             61503 non-null  int64  
 19  ORGANIZATION_CATEGORY  61503 non-null  object 
 20  EXTERNAL_SCORE_1       26658 non-null  float64
 21  EXTERNAL_SCORE_2       61369 non-null  float64
 22  EXTERNAL_SCORE_3       49264 non-null  float64
dtypes: float64(8), int64(7), object(8)
memory usage: 10.8+ MB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 14761 entries, 0 to 14760
Data columns (total 23 columns):
 #   Column                 Non-Null Count  Dtype  
---  ------                 --------------  -----  
 0   U_ID                   14761 non-null  int64  
 1   FLAG                   14761 non-null  int64  
 2   CONTRACT_TYPE          14761 non-null  object 
 3   GENDER                 14761 non-null  object 
 4   NUM_OF_CHILDREN        14761 non-null  int64  
 5   INCOME                 14761 non-null  float64
 6   APPROVED_CREDIT        14761 non-null  float64
 7   LOAN_ANNUITY           14761 non-null  float64
 8   PRODUCT_PRICE          14741 non-null  float64
 9   INCOME_CATEGORY        14761 non-null  object 
 10  EDUCATION              14761 non-null  object 
 11  FAMILY_STATUS          14761 non-null  object 
 12  HOUSING_CATEGORY       14761 non-null  object 
 13  DAYS_AGE               14761 non-null  int64  
 14  DAYS_WORK              14761 non-null  int64  
 15  DAYS_REGISTRATION      14761 non-null  float64
 16  DAYS_SINCE_ID_CHANGE   14761 non-null  int64  
 17  APPLY_DAYS             14761 non-null  object 
 18  APPLY_HOUR             14761 non-null  int64  
 19  ORGANIZATION_CATEGORY  14761 non-null  object 
 20  EXTERNAL_SCORE_1       6449 non-null   float64
 21  EXTERNAL_SCORE_2       14742 non-null  float64
 22  EXTERNAL_SCORE_3       11842 non-null  float64
dtypes: float64(8), int64(7), object(8)
memory usage: 2.6+ MB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2872306 entries, 0 to 2872305
Data columns (total 7 columns):
 #   Column       Dtype  
---  ------       -----  
 0   SK_ID_PREV   int64  
 1   U_ID         int64  
 2   INST_NUMBER  int64  
 3   INST_DAYS    float64
 4   PAY_DAYS     float64
 5   AMT_INST     float64
 6   AMT_PAY      float64
dtypes: float64(4), int64(3)
memory usage: 153.4 MB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 350712 entries, 0 to 350711
Data columns (total 18 columns):
 #   Column                     Non-Null Count   Dtype  
---  ------                     --------------   -----  
 0   SK_ID_PREV                 350712 non-null  int64  
 1   U_ID                       350712 non-null  int64  
 2   CONTRACT_TYPE              350712 non-null  object 
 3   LOAN_ANNUITY               274103 non-null  float64
 4   APPLICATION                350712 non-null  float64
 5   APPROVED_CREDIT            350712 non-null  float64
 6   AMT_DOWN_PAYMENT           164205 non-null  float64
 7   PRODUCT_PRICE              271072 non-null  float64
 8   APPLY_DAYS                 350712 non-null  object 
 9   APPLY_HOUR                 350712 non-null  int64  
 10  CONTRACT_STATUS            350712 non-null  object 
 11  DAYS_DECISION              350712 non-null  int64  
 12  TERM_PAYMENT               274103 non-null  float64
 13  YIELD_GROUP                350712 non-null  object 
 14  FIRST_DRAW                 211407 non-null  float64
 15  FIRST_DUE                  211407 non-null  float64
 16  TERMINATION                211407 non-null  float64
 17  NFLAG_INSURED_ON_APPROVAL  211407 non-null  float64
dtypes: float64(10), int64(4), object(4)
memory usage: 48.2+ MB
None
None
None
None

1 - Preprocessing Data (1)

Dilakukan data cleaning dengan tujuan memperbaiki atau menghapus kesalahan, ketidakkonsistenan, dan ketidakakuratan dalam kumpulan data. Identifikasi hal ini cukup dilakukan pada dataset train.csv dan test.csv (karena yang wajib digunakan dalam analisis) serta dilakukan secara general berdasarkan eksplorasi awal. Jelaskan setiap bagian preprocessing yang dilakukan!

In [9]:
# Melihat missing values dari setiap data yang akan digunakan
display(df_tr.isnull().sum())
display(df_te.isnull().sum())
display(df_pre.isnull().sum())
display(df_his.isnull().sum())
U_ID                         0
FLAG                         0
CONTRACT_TYPE                0
GENDER                       0
NUM_OF_CHILDREN              0
INCOME                       0
APPROVED_CREDIT              0
LOAN_ANNUITY                 1
PRODUCT_PRICE               62
INCOME_CATEGORY              0
EDUCATION                    0
FAMILY_STATUS                0
HOUSING_CATEGORY             0
DAYS_AGE                     0
DAYS_WORK                    0
DAYS_REGISTRATION            0
DAYS_SINCE_ID_CHANGE         0
APPLY_DAYS                   0
APPLY_HOUR                   0
ORGANIZATION_CATEGORY        0
EXTERNAL_SCORE_1         34845
EXTERNAL_SCORE_2           134
EXTERNAL_SCORE_3         12239
dtype: int64
U_ID                        0
FLAG                        0
CONTRACT_TYPE               0
GENDER                      0
NUM_OF_CHILDREN             0
INCOME                      0
APPROVED_CREDIT             0
LOAN_ANNUITY                0
PRODUCT_PRICE              20
INCOME_CATEGORY             0
EDUCATION                   0
FAMILY_STATUS               0
HOUSING_CATEGORY            0
DAYS_AGE                    0
DAYS_WORK                   0
DAYS_REGISTRATION           0
DAYS_SINCE_ID_CHANGE        0
APPLY_DAYS                  0
APPLY_HOUR                  0
ORGANIZATION_CATEGORY       0
EXTERNAL_SCORE_1         8312
EXTERNAL_SCORE_2           19
EXTERNAL_SCORE_3         2919
dtype: int64
SK_ID_PREV                        0
U_ID                              0
CONTRACT_TYPE                     0
LOAN_ANNUITY                  76609
APPLICATION                       0
APPROVED_CREDIT                   0
AMT_DOWN_PAYMENT             186507
PRODUCT_PRICE                 79640
APPLY_DAYS                        0
APPLY_HOUR                        0
CONTRACT_STATUS                   0
DAYS_DECISION                     0
TERM_PAYMENT                  76609
YIELD_GROUP                       0
FIRST_DRAW                   139305
FIRST_DUE                    139305
TERMINATION                  139305
NFLAG_INSURED_ON_APPROVAL    139305
dtype: int64
SK_ID_PREV       0
U_ID             0
INST_NUMBER      0
INST_DAYS        0
PAY_DAYS       673
AMT_INST         0
AMT_PAY        673
dtype: int64

Imputation

Mengisi nilai numerik (LOAN_ANNUITY dan PRODUCT_PRICE) yang kosong dengan median

In [10]:
# Mengisi nilai LOAN_ANNUITY dan PRODUCT_PRICE 
fillannuitytr = df_tr['LOAN_ANNUITY'].median()
fillpricetr = df_tr['PRODUCT_PRICE'].median()
fillannuityte = df_te['LOAN_ANNUITY'].median()
fillpricete = df_te['PRODUCT_PRICE'].median()

df_tr['LOAN_ANNUITY'].fillna(fillannuitytr, inplace = True) 
df_tr['PRODUCT_PRICE'].fillna(fillpricetr, inplace = True) 

df_te['LOAN_ANNUITY'].fillna(fillannuityte, inplace = True)
df_te['PRODUCT_PRICE'].fillna(fillpricete, inplace = True) 

display(df_tr.isna().sum(), df_te.isna().sum())
U_ID                         0
FLAG                         0
CONTRACT_TYPE                0
GENDER                       0
NUM_OF_CHILDREN              0
INCOME                       0
APPROVED_CREDIT              0
LOAN_ANNUITY                 0
PRODUCT_PRICE                0
INCOME_CATEGORY              0
EDUCATION                    0
FAMILY_STATUS                0
HOUSING_CATEGORY             0
DAYS_AGE                     0
DAYS_WORK                    0
DAYS_REGISTRATION            0
DAYS_SINCE_ID_CHANGE         0
APPLY_DAYS                   0
APPLY_HOUR                   0
ORGANIZATION_CATEGORY        0
EXTERNAL_SCORE_1         34845
EXTERNAL_SCORE_2           134
EXTERNAL_SCORE_3         12239
dtype: int64
U_ID                        0
FLAG                        0
CONTRACT_TYPE               0
GENDER                      0
NUM_OF_CHILDREN             0
INCOME                      0
APPROVED_CREDIT             0
LOAN_ANNUITY                0
PRODUCT_PRICE               0
INCOME_CATEGORY             0
EDUCATION                   0
FAMILY_STATUS               0
HOUSING_CATEGORY            0
DAYS_AGE                    0
DAYS_WORK                   0
DAYS_REGISTRATION           0
DAYS_SINCE_ID_CHANGE        0
APPLY_DAYS                  0
APPLY_HOUR                  0
ORGANIZATION_CATEGORY       0
EXTERNAL_SCORE_1         8312
EXTERNAL_SCORE_2           19
EXTERNAL_SCORE_3         2919
dtype: int64

Mengisi nilai external score 1,2, dan 3 dengan 0. Hal ini didasarkan pada keterbatasan data eksternal. Sehingga untuk analisis yang lebih konservatif serta meminimalisasi risiko untuk pemberian pinjaman, diisi nilai 0.

In [11]:
# Mengisi nilai external score 1,2, dan 3 dengan nilai 0
df_tr['EXTERNAL_SCORE_1'].fillna(0, inplace=True)
df_tr['EXTERNAL_SCORE_2'].fillna(0, inplace=True)
df_tr['EXTERNAL_SCORE_3'].fillna(0, inplace=True)
df_te['EXTERNAL_SCORE_1'].fillna(0, inplace=True)
df_te['EXTERNAL_SCORE_2'].fillna(0, inplace=True)
df_te['EXTERNAL_SCORE_3'].fillna(0, inplace=True)
In [12]:
# Melihat data kosong setelah dilakukan imputasi
display(df_tr.isna().sum(), df_te.isna().sum())
U_ID                     0
FLAG                     0
CONTRACT_TYPE            0
GENDER                   0
NUM_OF_CHILDREN          0
INCOME                   0
APPROVED_CREDIT          0
LOAN_ANNUITY             0
PRODUCT_PRICE            0
INCOME_CATEGORY          0
EDUCATION                0
FAMILY_STATUS            0
HOUSING_CATEGORY         0
DAYS_AGE                 0
DAYS_WORK                0
DAYS_REGISTRATION        0
DAYS_SINCE_ID_CHANGE     0
APPLY_DAYS               0
APPLY_HOUR               0
ORGANIZATION_CATEGORY    0
EXTERNAL_SCORE_1         0
EXTERNAL_SCORE_2         0
EXTERNAL_SCORE_3         0
dtype: int64
U_ID                     0
FLAG                     0
CONTRACT_TYPE            0
GENDER                   0
NUM_OF_CHILDREN          0
INCOME                   0
APPROVED_CREDIT          0
LOAN_ANNUITY             0
PRODUCT_PRICE            0
INCOME_CATEGORY          0
EDUCATION                0
FAMILY_STATUS            0
HOUSING_CATEGORY         0
DAYS_AGE                 0
DAYS_WORK                0
DAYS_REGISTRATION        0
DAYS_SINCE_ID_CHANGE     0
APPLY_DAYS               0
APPLY_HOUR               0
ORGANIZATION_CATEGORY    0
EXTERNAL_SCORE_1         0
EXTERNAL_SCORE_2         0
EXTERNAL_SCORE_3         0
dtype: int64
In [13]:
# Melihat Distribusi Kategori Organisasi untuk Kategori Pendapatan Pensioner
pensioner_data = df_tr[df_tr['INCOME_CATEGORY'] == 'Pensioner']
pension_organization_counts = pensioner_data['ORGANIZATION_CATEGORY'].value_counts()
print("Distribusi Kategori Organisasi untuk Kategori Pendapatan Pensioner:")
print(pension_organization_counts)
Distribusi Kategori Organisasi untuk Kategori Pendapatan Pensioner:
ORGANIZATION_CATEGORY
NA1       11248
School        1
Name: count, dtype: int64
In [14]:
# Mengganti 'NA1' with 'No Organizational Contract' pada df_tr dan df_te
df_tr.loc[df_tr['INCOME_CATEGORY'] == 'Pensioner', 'ORGANIZATION_CATEGORY'] = df_tr.loc[df_tr['INCOME_CATEGORY'] == 'Pensioner', 'ORGANIZATION_CATEGORY'].replace('NA1', 'No Organizational Contract')
df_te.loc[df_te['INCOME_CATEGORY'] == 'Pensioner', 'ORGANIZATION_CATEGORY'] = df_te.loc[df_te['INCOME_CATEGORY'] == 'Pensioner', 'ORGANIZATION_CATEGORY'].replace('NA1', 'No Organizational Contract')

2 - Exploratory Data Analysis (EDA)

Tahapan ini dilakukan untuk mendapatkan insight yang bermanfaat dari data. Pada tahapan ini anda harus membuat setidaknya 5 pertanyaan tambahan yang akan dijawab berdasarkan hasil eksplorasi dan visualisasi selain 3 pertanyaan wajib

2.1 - Bagaimana sebaran proporsi pada label target (FLAG)?

In [15]:
# Membuat plot sebaran flag
sns.countplot(x=df_tr['FLAG'], data=df_tr, palette="tab10")
plt.title("Distribution of The Flag")
plt.show()
No description has been provided for this image
In [16]:
# Melihat nilai pada setiap flag
df_tr["FLAG"].value_counts()
Out[16]:
FLAG
0    56534
1     4969
Name: count, dtype: int64

Dapat dilihat dari plot dan perbandingan jumlah untuk 0(membayar tepat waktu) dan 1(tidak membayar) sangat berbeda jauh. Hal ini bisa dijadikan pertimbangan nantinya untuk handle imbalanced data untuk meningkatkan F1 Score dari model

JAWAB:

2.2 - Bagaimana pola jumlah pinjaman yang disetujui (APPROVED_CREDIT) dan karakteristiknya berdasarkan jenis kontrak (CONTRACT_TYPE)?

Akan dianalisis pola approved credit berdasarkan rata-rata approved credit berdasarkan contract type nya. Sebelum itu, dilakukan outlier analysis terlebih dahulu menggunakan boxplot

In [17]:
# Membuat boxplot dan melihat apakah ada outlier
plt.figure(figsize=(8, 6))
sns.boxplot(data=df_tr, x='CONTRACT_TYPE', y='APPROVED_CREDIT', palette='Set2')
plt.title('Boxplot APPROVED_CREDIT berdasarkan CONTRACT_TYPE')
plt.xlabel('CONTRACT_TYPE')
plt.ylabel('APPROVED_CREDIT')
plt.grid(True)
plt.show()
No description has been provided for this image

Membuat data train copy yang untuk melihat mean yang sebenarnya setelah diganti nilai outlier dengan winsorize

In [18]:
from scipy.stats.mstats import winsorize

# Melakukan Winsorizing untuk menggantikan outlier
df_winsorized = df_tr.copy()
df_winsorized['APPROVED_CREDIT'] = winsorize(df_winsorized['APPROVED_CREDIT'], limits=[0.05, 0.05])

# Membuat boxplot setelah Winsorizing
plt.figure(figsize=(8, 6))
sns.boxplot(data=df_winsorized, x='CONTRACT_TYPE', y='APPROVED_CREDIT', palette='Set2')
plt.title('Boxplot APPROVED_CREDIT berdasarkan CONTRACT_TYPE (Winsorized)')
plt.xlabel('CONTRACT_TYPE')
plt.ylabel('APPROVED_CREDIT')
plt.grid(True)
plt.show()
No description has been provided for this image

Terlihat outlier berkurang dan akan dilihat distribution plot yang sudah tertanam deskriptif statistik nya

In [19]:
import seaborn as sns
import matplotlib.pyplot as plt

# Fungsi untuk menggambar garis-garis vertikal pada setiap subplot menggunakan deskriptif statistik winsorized
df_summarywin = df_winsorized.describe()
def draw_axvlines(plot, col):
    mean = df_summarywin.loc["mean", col]
    q1 = df_summarywin.loc["25%", col]
    q2 = df_summarywin.loc["50%", col]
    q3 = df_summarywin.loc["75%", col]
    plot.axvline(mean, color="black", linestyle='dashed', label="Mean")
    plot.axvline(q1, color="red", linestyle='dashed', label="25%")
    plot.axvline(q2, color="blue", linestyle='dashed', label="50%")
    plot.axvline(q3, color="purple", linestyle='dashed', label="75%")
    plot.legend()

# Membuat distribusi plot berdasarkan tipe pinjaman pada data train
g = sns.FacetGrid(df_tr, col="CONTRACT_TYPE", col_wrap=4, height=4)
g.map(sns.distplot, "APPROVED_CREDIT", bins=10, color="teal")

# Menambahkan garis-garis vertikal pada setiap subplot
for ax, col in zip(g.axes.flat, df_tr["CONTRACT_TYPE"].unique()):
    draw_axvlines(ax, "APPROVED_CREDIT")

# Menampilkan plot
plt.show()
No description has been provided for this image

Hal ini bertujuan untuk menggunakan mean setelah dilakukan outlier analysis. Seperti yang kita ketahui, mean sangat berpengaruh terhadap adanya outlier. Ketika outlier sangat jauh, maka mean akan berubah drastis. Oleh karena itu, kita sebenarnya ingin mencari mean sesungguhnya dan meletakkannya di dalam plot

In [20]:
# Melihat rata-rata sesungguhnya dari APPROVED CREDIT berdasarkan CONTRACT_TYPE setelah dilakukan outlier analysis.
df_winsorized.groupby("CONTRACT_TYPE")["APPROVED_CREDIT"].mean().reset_index()
Out[20]:
CONTRACT_TYPE APPROVED_CREDIT
0 Cash loans 610665.238550
1 Revolving loans 321846.140593
In [21]:
# Melihat proporsi rata-rata APPROVED_CREDIT berdasarkan CONTRACT_TYPE
plt.figure(figsize=(8, 6))
sns.barplot(x="CONTRACT_TYPE", y="APPROVED_CREDIT", data=df_tr.groupby("CONTRACT_TYPE")["APPROVED_CREDIT"].mean().reset_index(), palette="viridis")
plt.xlabel("CONTRACT_TYPE")
plt.ylabel("Rata-rata APPROVED_CREDIT")
plt.title("Rata-rata APPROVED_CREDIT berdasarkan CONTRACT_TYPE")
plt.show()
No description has been provided for this image

JAWAB:
Dapat terlihat rata-rata kredit yang diapproved berdasarkan EDA yang sudah dilakukan. Terlihat juga jika semakin tinggi rata-rata nya pada jenis contract type, maka bisa disimpulkan kemungkinan semakin tinggi juga approved credit yang akan diberikan kepada pelanggan.

2.3 - Bagaimana korelasi antar fitur? Fitur apa yang berkorelasi kuat serta bagaimana tindakannya? (merujuk pada penjelasan TUGAS)

In [22]:
# Melihat kolom mana saja yang termasuk ke dalam numerik
numeric_columns = df_tr.select_dtypes(include=['int64', 'float64'])

# Menampilkan kolom numerik dan juga korelasi antar atribut numerik nya
display(numeric_columns, numeric_columns.corr())
U_ID FLAG NUM_OF_CHILDREN INCOME APPROVED_CREDIT LOAN_ANNUITY PRODUCT_PRICE DAYS_AGE DAYS_WORK DAYS_REGISTRATION DAYS_SINCE_ID_CHANGE APPLY_HOUR EXTERNAL_SCORE_1 EXTERNAL_SCORE_2 EXTERNAL_SCORE_3
0 333538 0 1 67500.0 202500.0 10125.0 202500.0 -11539 -921 -119.0 -2757 18 0.572805 0.608276 0.000000
1 406644 0 1 202500.0 976711.5 49869.0 873000.0 -15743 -4482 -1797.0 -2455 14 0.655600 0.684298 0.000000
2 259130 0 0 180000.0 407520.0 25060.5 360000.0 -20775 365243 -8737.0 -4312 14 0.000000 0.580687 0.749022
3 411997 0 0 225000.0 808650.0 26086.5 675000.0 -20659 -10455 -4998.0 -4010 10 0.000000 0.623740 0.710674
4 241559 0 0 135000.0 180000.0 9000.0 180000.0 -9013 -1190 -3524.0 -1644 11 0.175511 0.492994 0.085595
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
61498 251159 0 2 360000.0 450000.0 44509.5 450000.0 -14268 -5813 -2313.0 -2709 6 0.295858 0.104877 0.616122
61499 441376 1 0 441000.0 904500.0 38452.5 904500.0 -21582 365243 -4106.0 -4191 12 0.693816 0.411595 0.239226
61500 184648 0 0 202500.0 900000.0 45000.0 900000.0 -17729 -1564 -11844.0 -1263 16 0.802154 0.714627 0.000000
61501 313981 0 0 94500.0 360000.0 17509.5 360000.0 -11258 -4159 -5060.0 -3698 15 0.000000 0.694125 0.105473
61502 293198 0 0 90000.0 203760.0 20281.5 180000.0 -24604 365243 -12794.0 -4105 11 0.000000 0.250811 0.644679

61503 rows × 15 columns

U_ID FLAG NUM_OF_CHILDREN INCOME APPROVED_CREDIT LOAN_ANNUITY PRODUCT_PRICE DAYS_AGE DAYS_WORK DAYS_REGISTRATION DAYS_SINCE_ID_CHANGE APPLY_HOUR EXTERNAL_SCORE_1 EXTERNAL_SCORE_2 EXTERNAL_SCORE_3
U_ID 1.000000 0.003173 -0.004228 0.007124 0.000192 0.002942 0.000611 -0.002354 0.000448 -0.005554 -0.003073 -0.002198 -0.002605 0.005246 0.004682
FLAG 0.003173 1.000000 0.018525 -0.020814 -0.039372 -0.019046 -0.047911 0.083524 -0.049952 0.039037 0.055748 -0.029929 -0.071108 -0.158043 -0.121743
NUM_OF_CHILDREN -0.004228 0.018525 1.000000 0.021530 0.007365 0.027298 0.003353 0.331271 -0.240094 0.178593 -0.036734 -0.009091 0.051042 -0.018072 -0.007816
INCOME 0.007124 -0.020814 0.021530 1.000000 0.279659 0.351514 0.285726 0.050382 -0.116870 0.053163 0.017501 0.064925 0.064734 0.120252 0.002131
APPROVED_CREDIT 0.000192 -0.039372 0.007365 0.279659 1.000000 0.771020 0.986744 -0.049237 -0.071783 0.014528 -0.007247 0.052435 0.093102 0.132789 0.059914
LOAN_ANNUITY 0.002942 -0.019046 0.027298 0.351514 0.771020 1.000000 0.775645 0.015773 -0.111406 0.045756 0.010126 0.052039 0.096143 0.127027 0.024260
PRODUCT_PRICE 0.000611 -0.047911 0.003353 0.285726 0.986744 0.775645 1.000000 -0.048109 -0.068953 0.016436 -0.009282 0.062366 0.098292 0.141129 0.061689
DAYS_AGE -0.002354 0.083524 0.331271 0.050382 -0.049237 0.015773 -0.048109 1.000000 -0.619288 0.324025 0.269007 0.088924 0.064010 -0.087376 -0.149740
DAYS_WORK 0.000448 -0.049952 -0.240094 -0.116870 -0.071783 -0.111406 -0.068953 -0.619288 1.000000 -0.206267 -0.271564 -0.087539 -0.099537 -0.026314 0.067213
DAYS_REGISTRATION -0.005554 0.039037 0.178593 0.053163 0.014528 0.045756 0.016436 0.324025 -0.206267 1.000000 0.099835 -0.010861 0.052048 -0.058184 -0.061473
DAYS_SINCE_ID_CHANGE -0.003073 0.055748 -0.036734 0.017501 -0.007247 0.010126 -0.009282 0.269007 -0.271564 0.099835 1.000000 0.030328 0.004554 -0.048741 -0.133738
APPLY_HOUR -0.002198 -0.029929 -0.009091 0.064925 0.052435 0.052039 0.062366 0.088924 -0.087539 -0.010861 0.030328 1.000000 0.039730 0.159600 -0.006245
EXTERNAL_SCORE_1 -0.002605 -0.071108 0.051042 0.064734 0.093102 0.096143 0.098292 0.064010 -0.099537 0.052048 0.004554 0.039730 1.000000 0.110419 0.043904
EXTERNAL_SCORE_2 0.005246 -0.158043 -0.018072 0.120252 0.132789 0.127027 0.141129 -0.087376 -0.026314 -0.058184 -0.048741 0.159600 0.110419 1.000000 0.095302
EXTERNAL_SCORE_3 0.004682 -0.121743 -0.007816 0.002131 0.059914 0.024260 0.061689 -0.149740 0.067213 -0.061473 -0.133738 -0.006245 0.043904 0.095302 1.000000
In [23]:
# Pisahkan Dataframe
databaru = {
    'Product Price': df_tr['PRODUCT_PRICE'],
    'Loan Annuity': df_tr['LOAN_ANNUITY'],
    'Approved Credit': df_tr['APPROVED_CREDIT']
}
df = pd.DataFrame(databaru)

# Buat Matriks korelasinya
correlation_matrix = df.corr()

# Plot Heatmapnya
plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='Oranges', fmt='.2f')
plt.title('Heatmap Correlation antara Product Price, Loan Annuity, dan Approved Credit')
plt.show()
No description has been provided for this image
In [24]:
# Membuat plot yang menunjukkan korelasi untuk APPROVED_CREDIT dan PRODUCT_PRICE
sns.regplot(x="APPROVED_CREDIT", y="PRODUCT_PRICE", data=df_tr)
plt.ylim(0,)
plt.show()
print(df_tr[["APPROVED_CREDIT", "PRODUCT_PRICE"]].corr())
No description has been provided for this image
                 APPROVED_CREDIT  PRODUCT_PRICE
APPROVED_CREDIT         1.000000       0.986744
PRODUCT_PRICE           0.986744       1.000000
In [25]:
# Membuat plot yang menunjukkan korelasi untuk LOAN_ANNUITY dan PRODUCT_PRICE
sns.regplot(x="LOAN_ANNUITY", y="PRODUCT_PRICE", data=df_tr)
plt.ylim(0,)
plt.show()
print(df_tr[["LOAN_ANNUITY", "PRODUCT_PRICE"]].corr())
No description has been provided for this image
               LOAN_ANNUITY  PRODUCT_PRICE
LOAN_ANNUITY       1.000000       0.775645
PRODUCT_PRICE      0.775645       1.000000
In [26]:
# Membuat plot yang menunjukkan korelasi untuk LOAN_ANNUITY dan PRODUCT_PRICE
sns.regplot(x="LOAN_ANNUITY", y="APPROVED_CREDIT", data=df_tr)
plt.ylim(0,)
plt.show()
print(df_tr[["LOAN_ANNUITY", "APPROVED_CREDIT"]].corr())
No description has been provided for this image
                 LOAN_ANNUITY  APPROVED_CREDIT
LOAN_ANNUITY          1.00000          0.77102
APPROVED_CREDIT       0.77102          1.00000

JAWAB:
Terdapat korelasi yang kuat antara 3 fitur, yaitu Product Price, Loan Annuity dan Approved Credit. Dalam hal ini, pemodelan klasifikasi nantinya hanya akan menggunakan fitur Product Price. Karena Product Price memiliki korelasi yang tinggi terhadap dua atribut lain yaitu Loan Annuity dan Approved Credit. Didapat nilai korelasi antara product price dengan approved credit yaitu 0.986786. Sedangkan, nilai korelasi antara product price dan loan annuity sebesar 0.776039. Dengan tingginya nilai korelasi ini diharapkan Product price menjadi sebuah atribut yang nantinya akan representatif terhadap baik itu Loan Annuity maupun Approved Credit. Selain itu dengan tingginya korelasi, memilih satu atribut saja yaitu Product Price bisa mengurangi redundansi atau data yang duplikat.

Untuk alasan teknis, Product Price bisa digunakan sebagai pintu awal pemberian pinjaman. Ketika sebuah perusahaan tahu orang ingin membeli produk apa, maka sebuah perusahaan bisa memperkirakan anuitas selanjutnya dan kredit yang diberikan ke konsumen tentunya. Selain itu, kita juga jadi bisa tahu alasan kekosongan nilai loan annuity dan approved credit. Hal ini bisa saja terjadi karena tidak diapproved credit nya karena tidak tahu ingin membeli produk apa, sehingga tidak dapat anuitas dan approved_credit.

2.4 - Bagaimanakah Distribusi Usia Peminjam yang membayar tepat waktu dan tidak tepat waktu

In [27]:
plt.figure(figsize=(10, 8))
df1 = df_tr.copy()
df1['DAYS_AGE'] = abs(df1.copy()['DAYS_AGE'])

# KDE plot of loans that were repaid on time
sns.kdeplot(df1.loc[df_tr.copy()['FLAG'] == 0, 'DAYS_AGE'] / 365, label='flag = 0')

# KDE plot of loans which were not repaid on time
sns.kdeplot(df1.loc[df_tr.copy()['FLAG'] == 1, 'DAYS_AGE'] / 365, label='flag = 1')

# Labeling of plot
plt.style.use('ggplot')
plt.xlabel('Age (years)')
plt.ylabel('Density')
plt.title('Distribution of Ages')

# Display legend
plt.legend()

# Show plot
plt.show()
No description has been provided for this image
In [28]:
# Filter dataframe untuk setiap FLAG
ages_flag_0 = df_tr.loc[df_tr['FLAG'] == 0, 'DAYS_AGE'] / 365
ages_flag_1 = df_tr.loc[df_tr['FLAG'] == 1, 'DAYS_AGE'] / 365

# Mendapatkan nilai usia yang paling umum pada masing-masing FLAG
most_common_age_flag_0 = ages_flag_0.value_counts().idxmax()
most_common_age_flag_1 = ages_flag_1.value_counts().idxmax()

print("Usia peminjam yang paling sering dijumpai pada FLAG 0:", most_common_age_flag_0)
print("Usia peminjam yang paling sering dijumpai pada FLAG 1:", most_common_age_flag_1)
Usia peminjam yang paling sering dijumpai pada FLAG 0: -28.328767123287673
Usia peminjam yang paling sering dijumpai pada FLAG 1: -35.057534246575344

2.5 - Bagaimanakah hubungan antara Penghasilan dan Approved Credit berdasarkan Jumlah Anak

In [29]:
plt.figure(figsize=(10, 6))
sns.scatterplot(data=df_tr, x='INCOME', y='APPROVED_CREDIT', hue='NUM_OF_CHILDREN', palette='viridis', s=100)
plt.title('Hubungan antara INCOME dan APPROVED_CREDIT berdasarkan NUM_OF_CHILDREN')
plt.xlabel('INCOME')
plt.ylabel('APPROVED_CREDIT')
plt.legend(title='NUM_OF_CHILDREN')
plt.grid(True)
plt.show()
No description has been provided for this image
In [30]:
credit_by_child_max = df_tr.groupby('NUM_OF_CHILDREN')['APPROVED_CREDIT'].max()
credit_by_child_min = df_tr.groupby('NUM_OF_CHILDREN')['APPROVED_CREDIT'].min()

credit_by_child = pd.concat([credit_by_child_min, credit_by_child_max], axis=1)
credit_by_child.columns = ['APPROVED_CREDIT_MAX', 'APPROVED_CREDIT_MIN']

# Menemukan kisaran pendapatan tertinggi untuk persetujuan kredit
incomemin = df_tr.loc[df_tr['APPROVED_CREDIT'].idxmin(), 'INCOME']
incomemax = df_tr.loc[df_tr['APPROVED_CREDIT'].idxmax(), 'INCOME']

print("Persetujuan Kredit berdasarkan Jumlah Anak:")
print(credit_by_child)
print("\nKisaran Rentang Pendapatan Terendah-Tertinggi untuk Persetujuan Kredit:")
print(f"{incomemin} - {incomemax}")
Persetujuan Kredit berdasarkan Jumlah Anak:
                 APPROVED_CREDIT_MAX  APPROVED_CREDIT_MIN
NUM_OF_CHILDREN                                          
0                            45000.0            4031032.5
1                            45000.0            2695500.0
2                            45000.0            4050000.0
3                            50940.0            2341089.0
4                            76410.0            1800000.0
5                           125640.0            1546020.0
6                           152820.0             270000.0
7                           143910.0             450000.0
8                           239850.0             239850.0
9                           526491.0             526491.0

Kisaran Rentang Pendapatan Terendah-Tertinggi untuk Persetujuan Kredit:
90000.0 - 783000.0

Dapat dilihat jika jumlah kredit yang disetujui akan berbeda-beda tergantung jumlah anaknya. Terdapat penurunan yang lumayan signifikan ketika jumlah anak mulai dari 0-5 anak.

2.6 - Bagaimanakah distribusi dari masing-masing INCOME_CATEGORY terhadap APPROVED_CREDIT

In [31]:
df_tr['INCOME_CATEGORY'].value_counts()
Out[31]:
INCOME_CATEGORY
Working                 31621
Commercial associate    14217
Pensioner               11249
State servant            4407
Unemployed                  5
Student                     3
Businessman                 1
Name: count, dtype: int64

Karena frekuensi untuk Unemployed, Businessman dan Student terlalu sedikit, maka tidak divisualisasi. Hal ini disebabkan karena plot distribusi tidak bisa terbentuk, mengingat plot distribusi terbentuk dari histogram berbasis frekuensi.

In [32]:
def draw_axvlines(plot, col):
    df_summarywin = df_tr.describe()
    mean = df_summarywin.loc["mean", col]
    q1 = df_summarywin.loc["25%", col]
    q2 = df_summarywin.loc["50%", col]
    q3 = df_summarywin.loc["75%", col]
    plot.axvline(mean, color="black", linestyle='dashed', label="Mean")
    plot.axvline(q1, color="red", linestyle='dashed', label="25%")
    plot.axvline(q2, color="blue", linestyle='dashed', label="50%")
    plot.axvline(q3, color="purple", linestyle='dashed', label="75%")
    plot.legend()

# Membuat subplots berdasarkan kategori INCOME_CATEGORY pada APPROVED_CREDIT
df_tr_filtered = df_tr[~df_tr['INCOME_CATEGORY'].isin(['Unemployed', 'Student', 'Businessman'])]
unique_income_categories = df_tr_filtered['INCOME_CATEGORY'].unique()
fig, axs = plt.subplots(len(unique_income_categories), 1, figsize=(10, 4*len(unique_income_categories)), sharex=True)

for idx, category in enumerate(unique_income_categories):
    sns.histplot(data=df_tr_filtered[df_tr_filtered['INCOME_CATEGORY'] == category], x='APPROVED_CREDIT', kde=True, bins=30, color='skyblue', ax=axs[idx])
    axs[idx].set_title(f'Distribusi APPROVED_CREDIT untuk INCOME_CATEGORY = {category}')
    axs[idx].set_xlabel('APPROVED_CREDIT')
    axs[idx].set_ylabel('Frekuensi')
    draw_axvlines(axs[idx], 'APPROVED_CREDIT')
    axs[idx].grid(True)

plt.tight_layout()
plt.show()
No description has been provided for this image
In [33]:
print("Rata-Rata Approved Credit berdasarkan Income Category")
df_winsorized.groupby("INCOME_CATEGORY")["APPROVED_CREDIT"].mean().reset_index()
Rata-Rata Approved Credit berdasarkan Income Category
Out[33]:
INCOME_CATEGORY APPROVED_CREDIT
0 Businessman 540000.000000
1 Commercial associate 644261.893965
2 Pensioner 529429.243177
3 State servant 649079.323009
4 Student 727176.000000
5 Unemployed 852300.000000
6 Working 566046.033158

2.7 Bagaimana rasio dan persebaran tipe pendidikan peminjam terhadap keterlambatan pembayaran yang berimplikasi pada tidak diberinya pinjaman?

In [34]:
education_delay_ratio = df_tr.groupby('EDUCATION')['FLAG'].mean()
plt.figure(figsize=(8, 6))
sns.barplot(x=education_delay_ratio.index, y=education_delay_ratio.values, palette='Set2')
plt.title('Pengaruh Pendidikan terhadap Tingkat Keterlambatan Pembayaran')
plt.xlabel('Tingkat Pendidikan')
plt.ylabel('Rasio Keterlambatan Pembayaran')
plt.xticks(rotation=60)
plt.show()
No description has been provided for this image
In [35]:
education_flag_counts = df_tr.groupby(['EDUCATION', 'FLAG']).size().unstack(fill_value=0)
education_flag_counts['Rasio Keterlambatan'] = education_flag_counts[1] / education_flag_counts.sum(axis=1)
education_flag_counts
Out[35]:
FLAG 0 1 Rasio Keterlambatan
EDUCATION
Academic degree 33 1 0.029412
Higher education 14123 764 0.051320
Incomplete higher 1876 169 0.082641
Lower secondary 676 84 0.110526
Secondary / secondary special 39826 3951 0.090253

2.8 Bagaimanakah distribusi dari APPLY_DAYS terhadap FLAG?

In [36]:
# Distribusi hari dalam seminggu (APPLY_DAYS) terhadap persetujuan pemberian pinjaman

sns.set(style="whitegrid")
# Membuat Countplot
plt.figure(figsize=(10, 6))
sns.countplot(data=df_tr, x='APPLY_DAYS', hue='FLAG', palette='Set2')
plt.title('Distribusi Hari Aplikasi Kredit dalam Seminggu Berdasarkan Persetujuan Kredit (FLAG)')
plt.xlabel('Hari dalam Seminggu')
plt.ylabel('Jumlah Aplikasi')
plt.xticks(rotation=45)
plt.legend(title='FLAG', labels=['FLAG = 0', 'FLAG = 1'])
plt.grid(True)
plt.show()
No description has been provided for this image
In [37]:
apply_days_flag_count = df_tr.groupby(['APPLY_DAYS', 'FLAG']).size().unstack(fill_value=0)
apply_days_flag_count['Rasio Pinjaman Ditolak'] = apply_days_flag_count[1] / apply_days_flag_count.sum(axis=1)
print("Tabel Jumlah Aplikasi Kredit Berdasarkan Hari dalam Seminggu dan Persetujuan Kredit (FLAG):")
print(apply_days_flag_count)
Tabel Jumlah Aplikasi Kredit Berdasarkan Hari dalam Seminggu dan Persetujuan Kredit (FLAG):
FLAG           0    1  Rasio Pinjaman Ditolak
APPLY_DAYS                                   
FRIDAY      9186  807                0.080757
MONDAY      9463  771                0.075337
SATURDAY    6262  533                0.078440
SUNDAY      2920  235                0.074485
THURSDAY    9203  808                0.080711
TUESDAY     9881  957                0.088300
WEDNESDAY   9619  858                0.081894

Dapat dilihat jika para peminjam di Hari Selasa biasanya memiliki rasio peminjaman ditolak tertinggi dibanding yang lainnya.

2.9 Bagaimanakah persebaran peminjam berdasarkan sumber penghasilan dan berasal dari kategori organisasi/instansi apa?

In [38]:
# Distribusi kategori pendapatan (INCOME_CATEGORY) terhadap persetujuan pemberian pinjaman

sns.set(style="whitegrid")
# Membuat Countplot
plt.figure(figsize=(10, 6))
sns.countplot(data=df_tr, x='INCOME_CATEGORY', hue='FLAG', palette='Set2')
plt.title('Distribusi Hari Aplikasi Kredit dalam Seminggu Berdasarkan Persetujuan Kredit (FLAG)')
plt.xlabel('Hari dalam Seminggu')
plt.ylabel('Jumlah Aplikasi')
plt.xticks(rotation=45)
plt.legend(title='FLAG', labels=['FLAG = 0', 'FLAG = 1'])
plt.grid(True)
plt.show()
No description has been provided for this image
In [39]:
inc_cat_flag_count = df_tr.groupby(['INCOME_CATEGORY', 'FLAG']).size().unstack(fill_value=0)
inc_cat_flag_count['Rasio Pinjaman Ditolak'] = inc_cat_flag_count[1] / inc_cat_flag_count.sum(axis=1)
print("Tabel Jumlah Aplikasi Kredit Berdasarkan Kategori Penghasilan dan Persetujuan Kredit (FLAG):")
print(inc_cat_flag_count)
Tabel Jumlah Aplikasi Kredit Berdasarkan Kategori Penghasilan dan Persetujuan Kredit (FLAG):
FLAG                      0     1  Rasio Pinjaman Ditolak
INCOME_CATEGORY                                          
Businessman               1     0                0.000000
Commercial associate  13148  1069                0.075192
Pensioner             10671   578                0.051382
State servant          4157   250                0.056728
Student                   3     0                0.000000
Unemployed                4     1                0.200000
Working               28550  3071                0.097119

Unemployed memiliki tingkat pinjaman ditolak tertinggi meskipun dengan jumlah observasi yang sedikit.

In [40]:
# Distribusi kategori organisasi (ORGANIZATION_CATEGORY) terhadap persetujuan pemberian pinjaman

sns.set(style="whitegrid")
# Membuat Countplot
plt.figure(figsize=(12, 6))
sns.countplot(data=df_tr, x='ORGANIZATION_CATEGORY', hue='FLAG', palette='Set2')
plt.title('Distribusi Hari Aplikasi Kredit dalam Seminggu Berdasarkan Persetujuan Kredit (FLAG)')
plt.xlabel('Hari dalam Seminggu')
plt.ylabel('Jumlah Aplikasi')
plt.xticks(rotation=90)
plt.legend(title='FLAG', labels=['FLAG = 0', 'FLAG = 1'])
plt.grid(True)
plt.show()
No description has been provided for this image
In [41]:
org_cat_flag_count = df_tr.groupby(['ORGANIZATION_CATEGORY', 'FLAG']).size().unstack(fill_value=0)
org_cat_flag_count['Rasio Pinjaman Ditolak'] = org_cat_flag_count[1] / org_cat_flag_count.sum(axis=1)
org_cat_flag_count_sorted = org_cat_flag_count.sort_values(by='Rasio Pinjaman Ditolak', ascending=False)
print("Tabel Jumlah Aplikasi Kredit Berdasarkan Kategori Organisasi terkait dan Persetujuan Kredit (FLAG):")
print(org_cat_flag_count_sorted)
Tabel Jumlah Aplikasi Kredit Berdasarkan Kategori Organisasi terkait dan Persetujuan Kredit (FLAG):
FLAG                            0     1  Rasio Pinjaman Ditolak
ORGANIZATION_CATEGORY                                          
Cleaning                       37    10                0.212766
Religion                       12     3                0.200000
NA1                             4     1                0.200000
Industry: type 8                5     1                0.166667
Industry: type 4              167    28                0.143590
Industry: type 1              176    27                0.133005
Transport: type 3             208    30                0.126050
Construction                 1209   165                0.120087
Industry: type 10              22     3                0.120000
Restaurant                    310    42                0.119318
Industry: type 3              564    74                0.115987
Advertising                    84    11                0.115789
Realtor                        70     9                0.113924
Legal Services                 64     8                0.111111
Mobile                         59     7                0.106061
Housing                       548    63                0.103110
Trade: type 1                  70     8                0.102564
Transport: type 4             918   102                0.100000
Industry: type 2               82     9                0.098901
Self-employed                6949   751                0.097532
Security                      538    58                0.097315
Trade: type 7                1410   147                0.094412
Business Entity Type 2       1888   195                0.093615
Trade: type 3                 601    62                0.093514
Agriculture                   457    47                0.093254
Business Entity Type 3      12301  1260                0.092914
Business Entity Type 1       1077   108                0.091139
Culture                        61     6                0.089552
Other                        3024   281                0.085023
Services                      276    25                0.083056
Hotel                         178    16                0.082474
Electricity                   184    16                0.080000
Kindergarten                 1281   107                0.077089
Postal                        382    31                0.075061
Industry: type 7              223    18                0.074689
Industry: type 9              612    48                0.072727
Trade: type 4                  13     1                0.071429
Transport: type 2             405    31                0.071101
Trade: type 2                 345    26                0.070081
Medicine                     2071   153                0.068795
Government                   1910   135                0.066015
Telecom                       117     8                0.064000
Industry: type 11             499    34                0.063790
Industry: type 13              16     1                0.058824
Industry: type 5              114     7                0.057851
School                       1688    99                0.055400
University                    295    16                0.051447
No Organizational Contract  10670   578                0.051387
Emergency                     113     6                0.050420
Police                        434    23                0.050328
Security Ministries           379    20                0.050125
Transport: type 1              39     2                0.048780
Trade: type 6                 127     6                0.045113
Industry: type 6               23     1                0.041667
Military                      518    22                0.040741
Bank                          506    20                0.038023
Insurance                     117     2                0.016807
Industry: type 12              75     1                0.013158
Trade: type 5                   9     0                0.000000

3 - Preprocessing Data (2)

  • Pada bagian ini dilakukan pra-pemrosesan data dan feature engineering melanjutkan proses data cleaning sebelumnya untuk digunakan dalam proses pemodelan berdasarkan insight yang diperoleh pada bagian EDA.
  • Peserta diwajibkan membuat setidaknya 3 fitur baru, dengan mengekstrak nilai fitur dari dataset train.csv, test.csv, previous_application.csv dan payment_history.csv. Transformasi data dengan mengubah satuan (seperti hari menjadi tahun) tidak dihitung sebagai pembentukan fitur baru. Dataset utama dalam analisis ini adalah train.csv dan test.csv sehingga hasil fitur baru digabungkan dengan setiap dataset ini (tidak boleh salah satu).
3.1 Membuat Fitur PRE_LATE pada data test dan train secara bersamaan
In [42]:
import pandas as pd

# Membuat fitur PRE_LATE berdasarkan pembayaran yang terlambat
df_his['LATE'] = (df_his['PAY_DAYS'] < df_his['INST_DAYS']).astype(int)

# Mengagregasi informasi apakah ada keterlambatan untuk setiap U_ID
df_pre_late = df_his.groupby('U_ID')['LATE'].sum().reset_index()
df_pre_late['PRE-LATE'] = (df_pre_late['LATE'] > 0).astype(int)
df_pre_late.drop(columns='LATE', inplace=True)

# Gabungkan fitur PRE_LATE ke dataset train dan test
df_tr = df_tr.merge(df_pre_late, on='U_ID', how='left')
df_te = df_te.merge(df_pre_late, on='U_ID', how='left')

# Mengimputasi nilai NA pada fitur PRE_LATE dengan 0
df_tr['PRE-LATE'].fillna(0, inplace=True)
df_te['PRE-LATE'].fillna(0, inplace=True)

# Pastikan tipe data PRE_LATE adalah object
df_tr['PRE-LATE'] = df_tr['PRE-LATE'].astype(str)
df_te['PRE-LATE'] = df_te['PRE-LATE'].astype(str)
3.2 Membuat atribut baru berdasarkan anomalitas (keanehan) DAYS_WORK pada data test dan train secara bersamaan menjadi DAYS_WORK_ANOM
In [43]:
df_tr['DAYS_WORK'].value_counts()
Out[43]:
DAYS_WORK
 365243    11253
-195          36
-384          35
-130          34
-222          33
           ...  
-8312          1
-6848          1
-9930          1
-7641          1
-5926          1
Name: count, Length: 8524, dtype: int64

Terdapat sebuah keanehan pada atribut ini. Days_Work menggambarkan berapa lama peminjam sudah bekerja sejak meminjam. Hal ini mengindikasikan adanya pengalaman bekerja dari para peminjam dalam hitungan hari. Setelah ditelusuri, angka negatif ini mengartikan bahwa pengambilan data dilakukan secara retrospektif atau melihat ke belakang. Oleh karena itu, melihat ke belakang sama saja mendapatkan nilai negatif berdasarkan selisih hari saat meminjam hingga pertama kali bekerja di masa lalu.

Terdapat keanehan pada nilai 365243 yang memiliki nilai positif dan sangat besar. Data ini mengalami sebuah kesalahan, oleh karena itu akan dibuat sebuah variabel baru yaitu anomalitas days_work. Sementara itu, kita akan mengganti nilai 365243 menjadi NaN dan akan dilakukan imputasi saat preprocessing (3)

In [44]:
# Membuat fitur DAYS_EMPLOYED_ANOM di df_tr dan df_te
df_tr['DAYS_EMPLOYED_ANOM'] = (df_tr["DAYS_WORK"] != 365243).astype(int)
df_te['DAYS_EMPLOYED_ANOM'] = (df_te["DAYS_WORK"] != 365243).astype(int)
In [45]:
# Mengganti nilai 365243 pada df_tr dan df_te menjadi NaN dan akan diimputasi di Preprocessing (3)
df_tr['DAYS_WORK'].replace({365243: np.nan}, inplace = True)
df_te['DAYS_WORK'].replace({365243: np.nan}, inplace = True)

3.3 Membuat fitur CREDIT_TERM pada data train dan test yang mengindikasikan jangka waktu dari pinjaman

In [46]:
# Membagi loan annuity dan approved credit menjadi credit term
df_tr['CREDIT_TERM'] = df_tr['LOAN_ANNUITY']/df_tr['APPROVED_CREDIT']
df_te['CREDIT_TERM'] = df_te['LOAN_ANNUITY']/df_te['APPROVED_CREDIT']

3.4 Membuat fitur HAS_LOAN_BEFORE berdasarkan Previous Applications

In [47]:
# Membuat fitur apakah pernah mengajukan pinjaman sebelumnya
df_tr['HAS_PREVIOUS_LOAN'] = df_tr['U_ID'].isin(df_pre['U_ID']).astype(object)
df_te['HAS_PREVIOUS_LOAN'] = df_te['U_ID'].isin(df_pre['U_ID']).astype(object)

Fitur ini menunjukkan apakah peminjam pernah melakukan peminjaman sebelumnya berdasarkan data Previous Applications

3.5 Membuat fitur DAYS_EMPLOYED_PERCENT

In [48]:
# Membagi lama bekerja dan usia menjadi persentase kepekerjaaan selama hidup
df_tr['DAYS_EMPLOYED_PERCENT'] = df_tr['DAYS_WORK'] / df_tr['DAYS_AGE']
df_te['DAYS_EMPLOYED_PERCENT'] = df_te['DAYS_WORK'] / df_te['DAYS_AGE']

Hal ini berarti persentase seseorang menghabiskan usianya untuk bekerja selama hidupnya. Bisa diartikan sebagai pengalaman bekerja juga

4 - Modelling

Hanya menggunakan dataset train.csv

Splitting Data

  • Peserta bebas dalam pengaturan proporsi dan penggunaan random_state pada splitting data. Data latih (train) sekurang-kurangnya 60% dari train.csv
  • Agar mempermudah analisis, train.csv dibagi menjadi 2 bagian, yaitu data train dan data validasi (bukan tes).
In [49]:
numerical_cols = [cname for cname in df_tr.columns if df_tr[cname].dtype in ['int64', 'float64']]
numerical_cols
Out[49]:
['U_ID',
 'FLAG',
 'NUM_OF_CHILDREN',
 'INCOME',
 'APPROVED_CREDIT',
 'LOAN_ANNUITY',
 'PRODUCT_PRICE',
 'DAYS_AGE',
 'DAYS_WORK',
 'DAYS_REGISTRATION',
 'DAYS_SINCE_ID_CHANGE',
 'APPLY_HOUR',
 'EXTERNAL_SCORE_1',
 'EXTERNAL_SCORE_2',
 'EXTERNAL_SCORE_3',
 'DAYS_EMPLOYED_ANOM',
 'CREDIT_TERM',
 'DAYS_EMPLOYED_PERCENT']
In [50]:
cat_cols = [cname for cname in df_tr.columns if df_tr[cname].dtype in ['object']]
cat_cols
Out[50]:
['CONTRACT_TYPE',
 'GENDER',
 'INCOME_CATEGORY',
 'EDUCATION',
 'FAMILY_STATUS',
 'HOUSING_CATEGORY',
 'APPLY_DAYS',
 'ORGANIZATION_CATEGORY',
 'PRE-LATE',
 'HAS_PREVIOUS_LOAN']
In [51]:
X = df_tr.drop(columns= 'FLAG')
y = df_tr['FLAG']

X_train, X_val, y_train, y_val = (train_test_split(X, y, test_size=0.2, stratify=y, random_state=42))
In [52]:
from sklearn.utils import resample

X = pd.concat([X_train, y_train], axis=1)

# Memisahkan income kurang dari 50 dan lebih dari 50
Flag0 = X[X.FLAG==0]
Flag1 = X[X.FLAG==1]

# upsample lebihdari50
income_upsampled = resample(Flag1,
                          replace=True, # sample with replacement
                          n_samples=len(Flag0), # match number in majority class
                          random_state=50) # reproducible results

# kombinasikan lebih dari 50 dan kurang dari 50
upsampled = pd.concat([Flag0, income_upsampled])

# mengecek value counts
upsampled.FLAG.value_counts()
Out[52]:
FLAG
0    45227
1    45227
Name: count, dtype: int64

Preprocessing Data (3) - Pipeline

  • Beberapa tahapan preprocessing data menggunakan operator pipeline pada scikit-learn agar lebih mudah digunakan dan menghindari kemungkinan terjadinya kebocoran (leakage) data tes saat pemodelan.
  • Peserta diwajibkan menggunakan setidaknya 3 metode preprocessing data berbeda pada tahapan preprocessing bagian 3 (pipeline). Jika dilakukan imputasi missing value menggunakan modus untuk data kategorik dan mean untuk data numerik akan dihitung sebagai 1 metode preprocessing yang sama.
In [53]:
# Melihat tipe data pada X train
X_train.info()
<class 'pandas.core.frame.DataFrame'>
Index: 49202 entries, 36300 to 6451
Data columns (total 27 columns):
 #   Column                 Non-Null Count  Dtype  
---  ------                 --------------  -----  
 0   U_ID                   49202 non-null  int64  
 1   CONTRACT_TYPE          49202 non-null  object 
 2   GENDER                 49202 non-null  object 
 3   NUM_OF_CHILDREN        49202 non-null  int64  
 4   INCOME                 49202 non-null  float64
 5   APPROVED_CREDIT        49202 non-null  float64
 6   LOAN_ANNUITY           49202 non-null  float64
 7   PRODUCT_PRICE          49202 non-null  float64
 8   INCOME_CATEGORY        49202 non-null  object 
 9   EDUCATION              49202 non-null  object 
 10  FAMILY_STATUS          49202 non-null  object 
 11  HOUSING_CATEGORY       49202 non-null  object 
 12  DAYS_AGE               49202 non-null  int64  
 13  DAYS_WORK              40176 non-null  float64
 14  DAYS_REGISTRATION      49202 non-null  float64
 15  DAYS_SINCE_ID_CHANGE   49202 non-null  int64  
 16  APPLY_DAYS             49202 non-null  object 
 17  APPLY_HOUR             49202 non-null  int64  
 18  ORGANIZATION_CATEGORY  49202 non-null  object 
 19  EXTERNAL_SCORE_1       49202 non-null  float64
 20  EXTERNAL_SCORE_2       49202 non-null  float64
 21  EXTERNAL_SCORE_3       49202 non-null  float64
 22  PRE-LATE               49202 non-null  object 
 23  DAYS_EMPLOYED_ANOM     49202 non-null  int64  
 24  CREDIT_TERM            49202 non-null  float64
 25  HAS_PREVIOUS_LOAN      49202 non-null  object 
 26  DAYS_EMPLOYED_PERCENT  40176 non-null  float64
dtypes: float64(11), int64(6), object(10)
memory usage: 10.5+ MB
In [54]:
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OrdinalEncoder

# Mendefinisikan kolom numerik dan kategorikal
numeric_cols = ['NUM_OF_CHILDREN', 'INCOME', 'PRODUCT_PRICE', 'DAYS_AGE', 'DAYS_WORK', 'EXTERNAL_SCORE_1', 'EXTERNAL_SCORE_2', 'EXTERNAL_SCORE_3', 'DAYS_SINCE_ID_CHANGE', 'CREDIT_TERM', 'DAYS_EMPLOYED_PERCENT', 'DAYS_REGISTRATION']
categorical_cols = ['INCOME_CATEGORY', 'DAYS_EMPLOYED_ANOM', 'ORGANIZATION_CATEGORY', 'HAS_PREVIOUS_LOAN']

# Membangun pipeline untuk kolom numerik
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())])

# Membangun pipeline untuk kolom kategorikal
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='most_frequent')),
    ('onehot', OrdinalEncoder())])

# Menggabungkan transformer untuk kolom numerik dan kategorikal
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_cols),
        ('cat', categorical_transformer, categorical_cols)])

Pemodelan

  • Pilihlah metrik evaluasi yang Anda gunakan untuk menilai performa model (tahapan pemodelan serta pemilihan model terbaik) dan berikan penjelasan menyeluruh tentang alasan Anda memilih metrik tersebut.
  • Pilihlah model terbaik dengan membandingkan beberapa model klasifikasi (minimal 7 model yang berbeda) dan berikan penjelasan mengapa model tersebut dipilih sebagai yang terbaik.

Metrik evaluasi yang digunakan: F1-Score
karena seperti yang kita ketahui data kita beberapa ada yang tidak seimbang seperti pada flag, setelah dilakukan oversampling maka alangkah baiknya tetap menggunakan F1-Score. F1-score adalah harmonic mean dari presisi dan recall. Metrik ini memberikan keseimbangan antara presisi dan recall, dan berguna ketika kita ingin memperhatikan keduanya secara seimbang. Selain itu, menurut saya metrik ini juga sudah lumayan bisa merepresentasikan presisi dan recall dari model. F1-Score juga bagus digunakan saat distribusi kelas tidak seimbang.

In [55]:
# Membuat metrik evaluasi beserta nilai-nilai dari metrik evaluasinya tidak lupa dengan classification report
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, classification_report
def evaluate_classification(y_true, y_pred):
    accuracy = accuracy_score(y_true, y_pred)
    precision = precision_score(y_true, y_pred)
    recall = recall_score(y_true, y_pred)
    f1 = f1_score(y_true, y_pred)
    print(classification_report(y_true, y_pred))
    return {'Accuracy': accuracy, 'Precision': precision, 'Recall': recall, 'F1-score': f1}

1 - Random Forest

In [56]:
# Menggabungkan preprocessor dengan model
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
                           ('classifier', RandomForestClassifier())])

# Melatih model
model1 = pipeline.fit(X_train, y_train)
In [57]:
# Membuat prediksi
Yrf_pred = pipeline.predict(X_val)

# Cek laporan klasifikasi
evaluate_classification(y_val, Yrf_pred)
              precision    recall  f1-score   support

           0       0.92      1.00      0.96     11307
           1       0.38      0.01      0.02       994

    accuracy                           0.92     12301
   macro avg       0.65      0.50      0.49     12301
weighted avg       0.88      0.92      0.88     12301

Out[57]:
{'Accuracy': 0.9187870904804487,
 'Precision': 0.38095238095238093,
 'Recall': 0.008048289738430584,
 'F1-score': 0.015763546798029555}

2 - K-Nearest Neighbors (KNN)

In [58]:
from sklearn.neighbors import KNeighborsClassifier
In [59]:
# Menggabungkan preprocessor dengan model
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
                           ('classifier', KNeighborsClassifier())])

# Melatih model
model2 = pipeline.fit(X_train, y_train)
In [60]:
# Membuat prediksi
Yknn_pred = pipeline.predict(X_val)

# Cek laporan klasifikasi
evaluate_classification(y_val, Yknn_pred)
              precision    recall  f1-score   support

           0       0.92      0.99      0.95     11307
           1       0.24      0.04      0.07       994

    accuracy                           0.91     12301
   macro avg       0.58      0.51      0.51     12301
weighted avg       0.87      0.91      0.88     12301

Out[60]:
{'Accuracy': 0.9125274367937566,
 'Precision': 0.24050632911392406,
 'Recall': 0.03822937625754527,
 'F1-score': 0.06597222222222222}

3 - Decision Trees

In [61]:
from sklearn.tree import DecisionTreeClassifier
In [62]:
# Inisialisasi model
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
                           ('classifier', DecisionTreeClassifier())])

# Latih model
model3 = pipeline.fit(X_train, y_train)
In [63]:
# Membuat prediksi
Ydectree_pred = pipeline.predict(X_val)

# Cek laporan klasifikasi
evaluate_classification(y_val, Ydectree_pred)
              precision    recall  f1-score   support

           0       0.92      0.91      0.92     11307
           1       0.13      0.15      0.14       994

    accuracy                           0.85     12301
   macro avg       0.53      0.53      0.53     12301
weighted avg       0.86      0.85      0.85     12301

Out[63]:
{'Accuracy': 0.8503373709454516,
 'Precision': 0.12752858399296393,
 'Recall': 0.14587525150905434,
 'F1-score': 0.1360863444392304}

4 - Support Vector Machine (SVM)

In [64]:
from sklearn.svm import SVC
In [65]:
# Inisialisasi model
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
                           ('classifier', SVC())])

# Latih model
model4 = pipeline.fit(X_train, y_train)
In [66]:
# Membuat prediksi
Ysvm_pred = pipeline.predict(X_val)

# Cek laporan klasifikasi
evaluate_classification(y_val, Ysvm_pred)
              precision    recall  f1-score   support

           0       0.92      1.00      0.96     11307
           1       0.00      0.00      0.00       994

    accuracy                           0.92     12301
   macro avg       0.46      0.50      0.48     12301
weighted avg       0.84      0.92      0.88     12301

Out[66]:
{'Accuracy': 0.9191935614990652,
 'Precision': 0.0,
 'Recall': 0.0,
 'F1-score': 0.0}

5 - Logistic Regression

In [67]:
from sklearn.linear_model import LogisticRegression
In [68]:
# Inisialisasi model
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
                           ('classifier', LogisticRegression())])

# Latih model
model5 = pipeline.fit(X_train, y_train)
In [69]:
# Membuat prediksi
Ylog_pred = pipeline.predict(X_val)

# Cek laporan klasifikasi
evaluate_classification(y_val, Ylog_pred)
              precision    recall  f1-score   support

           0       0.92      1.00      0.96     11307
           1       0.00      0.00      0.00       994

    accuracy                           0.92     12301
   macro avg       0.46      0.50      0.48     12301
weighted avg       0.84      0.92      0.88     12301

Out[69]:
{'Accuracy': 0.9191935614990652,
 'Precision': 0.0,
 'Recall': 0.0,
 'F1-score': 0.0}

6 - Naive Bayes

In [70]:
from sklearn.naive_bayes import GaussianNB
In [71]:
# Inisialisasi model
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
                           ('classifier', GaussianNB())])

# Latih model
model6 = pipeline.fit(X_train, y_train)
In [72]:
# Membuat prediksi
Ynb_pred = pipeline.predict(X_val)

# Cek laporan klasifikasi
evaluate_classification(y_val, Ynb_pred)
              precision    recall  f1-score   support

           0       0.93      0.91      0.92     11307
           1       0.21      0.27      0.23       994

    accuracy                           0.86     12301
   macro avg       0.57      0.59      0.58     12301
weighted avg       0.87      0.86      0.87     12301

Out[72]:
{'Accuracy': 0.8580603202991627,
 'Precision': 0.20625,
 'Recall': 0.2655935613682093,
 'F1-score': 0.23218997361477572}

7 - Gradient Boosting Machines (GBM)

In [73]:
from sklearn.ensemble import GradientBoostingClassifier
In [74]:
# Inisialisasi model
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
                           ('classifier', GradientBoostingClassifier())])

# Latih model
model7 = pipeline.fit(X_train, y_train)
In [75]:
# Membuat prediksi
Ygbm_pred = pipeline.predict(X_val)

# Cek laporan klasifikasi
evaluate_classification(y_val, Ygbm_pred)
              precision    recall  f1-score   support

           0       0.92      1.00      0.96     11307
           1       0.46      0.01      0.01       994

    accuracy                           0.92     12301
   macro avg       0.69      0.50      0.48     12301
weighted avg       0.88      0.92      0.88     12301

Out[75]:
{'Accuracy': 0.9191122672953418,
 'Precision': 0.46153846153846156,
 'Recall': 0.006036217303822937,
 'F1-score': 0.011916583912611719}

8 - Multilayer Perceptron (MLP)

In [76]:
from sklearn.neural_network import MLPClassifier

# Inisialisasi model
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
                           ('classifier', MLPClassifier())])

# Latih model
model8 = pipeline.fit(X_train, y_train)
In [77]:
# Membuat prediksi
Ymlp_pred = pipeline.predict(X_val)

# Cek laporan klasifikasi
evaluate_classification(y_val, Ymlp_pred)
              precision    recall  f1-score   support

           0       0.92      1.00      0.96     11307
           1       0.32      0.02      0.04       994

    accuracy                           0.92     12301
   macro avg       0.62      0.51      0.50     12301
weighted avg       0.87      0.92      0.88     12301

Out[77]:
{'Accuracy': 0.9169173237948134,
 'Precision': 0.3157894736842105,
 'Recall': 0.02414486921529175,
 'F1-score': 0.044859813084112146}

9 - Extreme Gradient Boosting (XGBoost):

In [78]:
from xgboost import XGBClassifier

# Inisialisasi model
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
                           ('classifier', XGBClassifier())])

# Latih model
model9 = pipeline.fit(X_train, y_train)
In [79]:
# Membuat prediksi
Yxgb_pred = pipeline.predict(X_val)

# Cek laporan klasifikasi
evaluate_classification(y_val, Yxgb_pred)
              precision    recall  f1-score   support

           0       0.92      0.99      0.96     11307
           1       0.35      0.04      0.07       994

    accuracy                           0.92     12301
   macro avg       0.64      0.52      0.51     12301
weighted avg       0.88      0.92      0.88     12301

Out[79]:
{'Accuracy': 0.9166734411836436,
 'Precision': 0.34951456310679613,
 'Recall': 0.03621730382293763,
 'F1-score': 0.06563354603463993}

10 - Ada Boost

In [80]:
from sklearn.ensemble import AdaBoostClassifier

# Inisialisasi model
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
                           ('classifier', AdaBoostClassifier())])

# Latih model
model10 = pipeline.fit(X_train, y_train)
In [81]:
# Membuat prediksi
Yada_pred = pipeline.predict(X_val)

# Cek laporan klasifikasi
evaluate_classification(y_val, Yada_pred)
              precision    recall  f1-score   support

           0       0.92      1.00      0.96     11307
           1       0.40      0.00      0.01       994

    accuracy                           0.92     12301
   macro avg       0.66      0.50      0.48     12301
weighted avg       0.88      0.92      0.88     12301

Out[81]:
{'Accuracy': 0.9190309730916185,
 'Precision': 0.4,
 'Recall': 0.004024144869215292,
 'F1-score': 0.00796812749003984}

11 - Quadratic Discriminant Analysis (QDA)

In [82]:
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis

# Inisialisasi model
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
                           ('classifier', QuadraticDiscriminantAnalysis())])


# Latih model
model11 = pipeline.fit(X_train, y_train)
In [83]:
# Membuat prediksi
Yqda_pred = pipeline.predict(X_val)

# Cek laporan klasifikasi
evaluate_classification(y_val, Yqda_pred)
              precision    recall  f1-score   support

           0       0.93      0.95      0.94     11307
           1       0.24      0.18      0.21       994

    accuracy                           0.89     12301
   macro avg       0.59      0.57      0.57     12301
weighted avg       0.87      0.89      0.88     12301

Out[83]:
{'Accuracy': 0.888952117714007,
 'Precision': 0.2445054945054945,
 'Recall': 0.1790744466800805,
 'F1-score': 0.2067363530778165}

Kumpulan semua model

In [84]:
from sklearn.pipeline import Pipeline
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
from sklearn.model_selection import KFold
import pandas as pd

def evaluate_models(X_train, y_train, X_val, y_val, models):
    results = {}
    
    for model_name, model in models.items():
        pipeline = Pipeline(steps=[('preprocessor', preprocessor),
                                   ('classifier', model)])
        
        # Melatih model menggunakan data pelatihan
        pipeline.fit(X_train, y_train)
        
        # Melakukan prediksi pada data uji
        y_pred = pipeline.predict(X_val)
        
        # Menghitung performa model
        accuracy = accuracy_score(y_val, y_pred)
        precision = precision_score(y_val, y_pred)
        recall = recall_score(y_val, y_pred)
        f1 = f1_score(y_val, y_pred)
        roc_auc = roc_auc_score(y_val, y_pred)
        
        # Menyimpan hasil performa model
        results[model_name] = {'Accuracy': accuracy,
                               'Precision': precision,
                               'Recall': recall,
                               'F1 Score': f1,
                               'ROC AUC Score': roc_auc}
        
    return pd.DataFrame(results).T

# Menyusun kandidat model
models = {
    'Random Forest': RandomForestClassifier(),
    'K Neighbors': KNeighborsClassifier(),
    'Decision Tree': DecisionTreeClassifier(),
    'SVM': SVC(),
    'Logistic Regression': LogisticRegression(),
    'Gaussian Naive Bayes': GaussianNB(),
    'Gradient Boosting': GradientBoostingClassifier(),
    'MLP': MLPClassifier(),
    'XGBoost': XGBClassifier(),
    'AdaBoost': AdaBoostClassifier(),
    'Quadratic Discriminant Analysis': QuadraticDiscriminantAnalysis()
}

# Memanggil fungsi untuk mengevaluasi model
evaluation_results = evaluate_models(X_train, y_train, X_val, y_val, models)

# Menampilkan hasil evaluasi dalam bentuk tabel
print(evaluation_results)
                                 Accuracy  Precision    Recall  F1 Score  \
Random Forest                    0.918950   0.400000  0.006036  0.011893   
K Neighbors                      0.912527   0.240506  0.038229  0.065972   
Decision Tree                    0.852207   0.129496  0.144869  0.136752   
SVM                              0.919194   0.000000  0.000000  0.000000   
Logistic Regression              0.919194   0.000000  0.000000  0.000000   
Gaussian Naive Bayes             0.858060   0.206250  0.265594  0.232190   
Gradient Boosting                0.919194   0.500000  0.006036  0.011928   
MLP                              0.918787   0.333333  0.005030  0.009911   
XGBoost                          0.916673   0.349515  0.036217  0.065634   
AdaBoost                         0.919031   0.400000  0.004024  0.007968   
Quadratic Discriminant Analysis  0.888952   0.244505  0.179074  0.206736   

                                 ROC AUC Score  
Random Forest                         0.502620  
K Neighbors                           0.513808  
Decision Tree                         0.529629  
SVM                                   0.500000  
Logistic Regression                   0.500000  
Gaussian Naive Bayes                  0.587869  
Gradient Boosting                     0.502753  
MLP                                   0.502073  
XGBoost                               0.515146  
AdaBoost                              0.501747  
Quadratic Discriminant Analysis       0.565216  

Model Terbaik

In [85]:
best = Pipeline(steps=[('preprocessor', preprocessor),
                           ('classifier', GaussianNB())]) # model terbaik
best.fit(X_train, y_train) # fitting model terbaik menggunakan data latih
Out[85]:
Pipeline(steps=[('preprocessor',
                 ColumnTransformer(transformers=[('num',
                                                  Pipeline(steps=[('imputer',
                                                                   SimpleImputer(strategy='median')),
                                                                  ('scaler',
                                                                   StandardScaler())]),
                                                  ['NUM_OF_CHILDREN', 'INCOME',
                                                   'PRODUCT_PRICE', 'DAYS_AGE',
                                                   'DAYS_WORK',
                                                   'EXTERNAL_SCORE_1',
                                                   'EXTERNAL_SCORE_2',
                                                   'EXTERNAL_SCORE_3',
                                                   'DAYS_SINCE_ID_CHANGE',
                                                   'CREDIT_TERM',
                                                   'DAYS_EMPLOYED_PERCENT',
                                                   'DAYS_REGISTRATION']),
                                                 ('cat',
                                                  Pipeline(steps=[('imputer',
                                                                   SimpleImputer(strategy='most_frequent')),
                                                                  ('onehot',
                                                                   OrdinalEncoder())]),
                                                  ['INCOME_CATEGORY',
                                                   'DAYS_EMPLOYED_ANOM',
                                                   'ORGANIZATION_CATEGORY',
                                                   'HAS_PREVIOUS_LOAN'])])),
                ('classifier', GaussianNB())])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.

5 - Evaluation

Lakukanlah uji performa model terbaik di atas dengan memprediksi data test.csv menggunakan setidaknya 3 metrik evaluasi yang berbeda dan berikan interpretasi.

In [86]:
X_test = df_te.drop(columns= 'FLAG')
y_test = df_te['FLAG']
          
# Predict the test set
Y_pred = best.predict(X_test)

# Check the classification report
print(classification_report(y_test, Y_pred))

accuracy = accuracy_score(y_test, Y_pred)
precision = precision_score(y_test, Y_pred)
recall = recall_score(y_test, Y_pred)
f1 = f1_score(y_test, Y_pred)
roc_auc = roc_auc_score(y_test, Y_pred)
conf_matrix = confusion_matrix(y_test, Y_pred)

# Menampilkan hasil evaluasi
print("Accuracy:", accuracy)
print("Precision:", precision)
print("Recall:", recall)
print("F1 Score:", f1)
print("ROC-AUC Score:", roc_auc)
              precision    recall  f1-score   support

           0       0.93      0.91      0.92     13558
           1       0.21      0.28      0.24      1203

    accuracy                           0.86     14761
   macro avg       0.57      0.59      0.58     14761
weighted avg       0.88      0.86      0.87     14761

Accuracy: 0.8575977237314545
Precision: 0.21277955271565496
Recall: 0.27680798004987534
F1 Score: 0.24060693641618497
ROC-AUC Score: 0.5929695601680266
In [87]:
# Menampilkan confusion matrix
print("Confusion Matrix:")
print(conf_matrix)
Confusion Matrix:
[[12326  1232]
 [  870   333]]

Interpretasi Terlihat jika nilai F1-Score naik dari awalnya 0.236959 menjadi 0.24692179700499167. Selain itu, terjadi kenaikan pada metrik evaluasi selain F1 Score. Jika F1-score naik diiringi kenaikan precision dan recall maka dapat diindikasikan jika data bisa dikatakan balance. Seperti yang kita ketahui F1 score adalah harmonic mean dari presisi dan recall. Hal ini karena sudah dilakukan balancing menggunakan oversampling di atas. ROC AUC juga mengalami kenaikan memiliki makna bahwa model sudah bisa mengukur seberapa baik model membedakan antara dua kelas. ROC AUC juga erguna saat kita memiliki masalah klasifikasi dengan distribusi kelas yang tidak seimbang dan ketika kita ingin mempertimbangkan semua tingkat ambang secara keseluruhan.

6 - Post Analysis

  • Berikan penjelasan tentang fitur-fitur yang paling penting pada model terbaik yang dipilih dan jelaskan bagaimana dan mengapa fitur-fitur tersebut sangat penting serta bagaimana pola pengaruhnya terhadap label target (FLAG)
  • Jalankan syntax berikut tanpa mengubah apapun. Tugas anda adalah memberikan interpretasi pada masing-masing output

Feature Importance

In [88]:
from sklearn.inspection import permutation_importance
import shap
In [89]:
def plot_fi(model, X = X_val, y = y_val, rep = 10):
    perm = permutation_importance(model, X, y, n_repeats=10, random_state=0, n_jobs=-1)

    perm2 = pd.DataFrame({'feature': X.columns.tolist()*rep, 'importance': perm["importances"].transpose().reshape(-1)})
    perm2["importance"] = perm2.importance/perm2.importance.sum()*rep
    urut = perm2.groupby("feature").mean().sort_values("importance", ascending=False)

    plt.figure(figsize=(8, 6))
    fig, axs = plt.subplots(1, 2, figsize=(25, 15))

    sns.barplot(x='importance', y='feature', data=perm2, order=urut.index, ax=axs[0])
    sns.stripplot(x='importance', y='feature', data=perm2, order = urut.index, s=5, ax=axs[1])
    plt.show()

    return urut
In [90]:
plot_fi(model = best, X = X_val, y = y_val)
<Figure size 800x600 with 0 Axes>
No description has been provided for this image
Out[90]:
importance
feature
DAYS_AGE 0.172836
DAYS_EMPLOYED_ANOM 0.114878
DAYS_WORK 0.105132
EXTERNAL_SCORE_3 0.103266
DAYS_SINCE_ID_CHANGE 0.087610
PRODUCT_PRICE 0.078486
DAYS_EMPLOYED_PERCENT 0.066459
INCOME_CATEGORY 0.065319
INCOME 0.059824
DAYS_REGISTRATION 0.048108
EXTERNAL_SCORE_1 0.029756
CREDIT_TERM 0.028720
NUM_OF_CHILDREN 0.013375
ORGANIZATION_CATEGORY 0.012960
EXTERNAL_SCORE_2 0.010057
HAS_PREVIOUS_LOAN 0.003214
LOAN_ANNUITY 0.000000
PRE-LATE 0.000000
APPLY_DAYS 0.000000
HOUSING_CATEGORY 0.000000
GENDER 0.000000
FAMILY_STATUS 0.000000
APPLY_HOUR 0.000000
EDUCATION 0.000000
CONTRACT_TYPE 0.000000
APPROVED_CREDIT 0.000000
U_ID 0.000000

DAYS_AGE Usia seseorang dapat menjadi indikasi stabilitas keuangan dan kemampuan untuk membayar pinjaman. Umumnya, semakin tua usia seseorang, semakin stabil keuangannya, yang dapat mengurangi risiko gagal bayar.

DAYS_EMPLOYED_ANOMpenting karena lamanya seseorang bekerja dapat mempengaruhi kemampuan mereka untuk membayar pinjaman. Anomali dalam lamanya bekerja mungkin menunjukkan informasi yang tidak konsisten atau tidak dapat dipercaya, yang dapat mempengaruhi keputusan pemberian pinjaman.

DAYS_WORK Seseorang yang telah bekerja untuk waktu yang lama mungkin memiliki stabilitas keuangan yang lebih besar dan kemampuan untuk membayar kembali pinjaman.

PRODUCT_PRICE penting karena jumlah pinjaman yang diminta dapat mempengaruhi kemampuan peminjam untuk membayar kembali pinjaman. Jumlah pinjaman yang lebih tinggi dapat mengindikasikan risiko yang lebih tinggi bagi pemberi pinjaman.

DAYS_EMPLOYED_PERCENT Penting karena fitur ini dapat memberikan informasi tentang stabilitas karier dan keuangan peminjam.

HAS_PREVIOUS_LOAN Penting karena dapat menunjukkan apakah peminjam memiliki pinjaman sebelumnya. Ini bisa menjadi indikasi pengalaman peminjam dengan pinjaman sebelumnya dan kemungkinan membayar kembali pinjaman saat ini.

Opsional -- SHAP values

Opsional -- SHAP values individual / SHAP Force Plot

7 - Conclusion & Recommendation

Berikan rekomendasi & kesimpulan menyeluruh terhadap bisnis PDM Paylater berdasarkan hasil analisis yang telah dilakukan.

KESIMPULAN: Kesimpulan yang dapat diperoleh adalah model Gaussian Naive Bayes adalah model terbaik yang bisa mengklasifikasikan para peminjam di PDM Paylater berdasarkan kriteria-kriteria peminjam dengan keberhasilan diberikan pinjaman. Telah dilakukan rangkaian operasi mulai dari Preprocessing, Preprocessing dengan Pipeline, hingga Modelling serta Uji Performa Model. Selanjutnya, juga sudah digambarkan seberapa penting atribut-atribut yang ada di dalam data terhadap keberhasilan pemberian pinjaman. Hal ini tergambar melalui tabel feature importance.


REKOMENDASI: Berdasarkan apa yang sudah dianalisis karena kegagalan pembayaran memiliki risiko yang tinggi terhadap kriteria peminjam PDM Paylater memang harus lumayan selektif dalam memilih atribut-atribut untuk para peminjam. Hal ini bisa dilihat dari feature importance yang menandakan seberapa berpengaruh atribut tersebut terhadap pemberian pinjaman. Selain itu, pengumpulan data eksternal dan juga kesalahan data harus terus diperhatikan agar tidak terjadi kesalahan.